161 Commits

Author SHA1 Message Date
徐涛
12ec8d26bf fix(charge):修正用户计费无法搜索的问题。 2022-09-29 16:14:41 +08:00
徐涛
e67b9afa68 fix(user):更正用户分页检索接口的权限。 2022-09-29 10:43:07 +08:00
徐涛
201af1cc25 fix(charge):修复记录列表中没有列出转供电企业的名称的问题。 2022-09-29 09:44:35 +08:00
徐涛
183092b670 enhance(user):清除已经不再使用的调试语句。 2022-09-29 09:43:27 +08:00
徐涛
8caf069b8a fix(charge):修复记录用户延期时用户列表无法列出用户名称的问题。 2022-09-29 09:42:11 +08:00
徐涛
11cc6f0de1 enhance(json):调整JSON编解码器的配置。 2022-09-29 06:32:50 +08:00
徐涛
00e2664007 fix(meter):调整路由定义顺序。 2022-09-28 21:22:14 +08:00
徐涛
746a9bcaa9 refactor(dep):整理迁移到fiber以后的依赖。 2022-09-28 20:50:49 +08:00
徐涛
7a8e8014ad refactor(god):天神模式控制器基本完成向fiber的迁移。 2022-09-28 20:35:46 +08:00
徐涛
c028056458 refactor(fee):附加费控制器基本完成向fiber的迁移。 2022-09-28 20:29:15 +08:00
徐涛
5c1b26c77f refactor(meter):终端表计控制器基本切换到fiber框架。 2022-09-28 20:14:02 +08:00
徐涛
122bc228bf refactor(park):园区部分的控制器基本完成向fiber框架的迁移。 2022-09-28 17:26:48 +08:00
徐涛
ba9c87d2b1 refactor(controller):完成部分控制器向fiber框架的迁移。 2022-09-28 17:13:26 +08:00
徐涛
e13193de6d refactor(charge):基本完成用户续费记录接口到fiber框架的切换。 2022-09-28 16:02:24 +08:00
徐涛
4b08952916 refactor(app):基本完成基础服务框架的功能构建。 2022-09-28 16:01:16 +08:00
徐涛
df9bf83bb8 fix(deprecates):继续清理已经删除的表计摊薄字段。 2022-09-23 15:53:56 +08:00
徐涛
0169419707 enhance(enduser):精简终端表计统计所使用的查询语句。 2022-09-23 10:26:36 +08:00
徐涛
2ba64227d0 enhance(fee):放开费用检索的权限。 2022-09-22 21:03:28 +08:00
徐涛
c56e1128ff fix(login):修正监管方登录返回的代码。 2022-09-22 20:57:33 +08:00
徐涛
7e861ba4e6 enhance(cache):修改附加费关联缓存的设计。 2022-09-22 20:26:51 +08:00
徐涛
712d704004 fix(enduser):调整更新终端用户调整电费统计部分的空指针问题。 2022-09-22 17:04:49 +08:00
徐涛
502a8bbcee fix(enduser):修正当调整电费也是空值时出现的空指针问题。 2022-09-22 16:16:14 +08:00
徐涛
c306a749aa fix(enduser):改正一个查询语句中的大小写问题。 2022-09-22 16:12:28 +08:00
徐涛
1385f0bbcb fix(enduser):修正调整电费统计中失误计入未发布报表的问题。 2022-09-22 15:01:58 +08:00
徐涛
0e7333b104 fix(park):修正两处不能保存内容的错误。 2022-09-22 14:40:23 +08:00
徐涛
de6e24dcd3 fix(enduser):修正当户址与户名为空的时候出现的空指针问题。 2022-09-22 14:14:34 +08:00
徐涛
f3457c9ab2 fix(fee):修正错误的文字。 2022-09-22 13:25:14 +08:00
徐涛
022788bb44 enhance(enduser):终端用户统计部分增加终端用户表计类型。 2022-09-22 10:19:25 +08:00
徐涛
f4ee7cf8a4 enhance(enduser):基本完成终端用户表计在一个时间阶段内的统计功能。 2022-09-22 09:56:33 +08:00
徐涛
f8f8a0ced1 enhance(fee):基本完成物业附加费部分的运算。 2022-09-21 22:46:33 +08:00
徐涛
38ec847f55 fix(publicity):更新公示报表中错误的json字段命名。 2022-09-21 16:54:33 +08:00
徐涛
d47a052d39 fix(migrate):修正数据库迁移脚本中的错误。 2022-09-21 16:49:30 +08:00
徐涛
9f68d3adea enhance(publicity):调整公示报表数据结构与计算。 2022-09-21 16:44:01 +08:00
徐涛
c716de21aa enhance(model):调整物业附加费、公示报表部分的数据结构模型,以及数据库迁移脚本。 2022-09-21 15:36:03 +08:00
徐涛
fa61f83c6a refactor(db):切换底层数据库驱动。 2022-09-21 08:47:24 +08:00
徐涛
f66782c87b enhance(utils):增加对切片进行分区的工具函数。 2022-09-20 17:12:20 +08:00
徐涛
875b013c2b fix(migrate);修正数据库迁移时默认数据迁移方向。 2022-09-20 16:22:46 +08:00
徐涛
8ec217534e enhance(calculate):基本完成摊薄总计部分的计算改动。 2022-09-20 15:44:25 +08:00
徐涛
2c182496fa enhance(model):调整数据结构使其方便迁移。 2022-09-20 15:09:00 +08:00
徐涛
c7569fbc2c enhance(model):修改报表及公示的数据结构,使其能够支持公共区域部分的计算。 2022-09-20 15:05:24 +08:00
徐涛
6740ab3f48 enhance(migrate):调整报表总计的数据库结构。
重要:下一次升级数据库,需要清除已经不再使用的报表总计列。
2022-09-20 14:56:03 +08:00
徐涛
aa3a4dc44e fix(enduser):修正抄表记录的初始化与上传。现在可以正常上传了。 2022-09-20 14:50:14 +08:00
徐涛
d7f3e0f096 fix(date):空日期改为2000年。 2022-09-20 14:24:43 +08:00
徐涛
efc0a605b7 fix(model):调整Decimal类型,防止bun框架将其认定为JSONB类型。 2022-09-20 12:31:55 +08:00
徐涛
b5c2455af7 enhance(meter):改进终端表计批量导入性能。 2022-09-20 11:03:38 +08:00
徐涛
a13193cfa4 enhance(publicity):向前端返回的终端用户数据增加公共设施与摊薄标记。 2022-09-20 06:39:57 +08:00
徐涛
794054b831 enhance(config):调整数据库连接池配置。 2022-09-19 22:26:27 +08:00
徐涛
aa5c43e51a fix(god):基本完成天神模式中的查询调整。 2022-09-19 20:55:32 +08:00
徐涛
8a070d4396 refactor(pg):更换数据库驱动为pgx。 2022-09-19 17:26:04 +08:00
徐涛
4620271fa5 fix(report):基本完成公示报表检索部分的查询。 2022-09-19 14:56:12 +08:00
徐涛
7f585c0452 fix(report):修正精简组装报表查询语句。 2022-09-19 14:41:49 +08:00
徐涛
d98e782f12 fix(withdraw):修正撤回管理中嵌套查询的问题。 2022-09-19 14:40:05 +08:00
徐涛
cafed0c7f0 doc(readme):更新数据嵌套的说明。 2022-09-19 14:38:48 +08:00
徐涛
8bd9c2f46f doc(readme):增加编写要点内容。 2022-09-19 14:29:43 +08:00
徐涛
0355300908 fix(report):基本完成报表部分的查询调整。 2022-09-19 14:27:25 +08:00
徐涛
3742a445b7 fix(meter):修正终端表计档案管理中无效的判断。 2022-09-19 12:51:05 +08:00
徐涛
01953132ca fix(meter):终端用户表计档案完成查询调整。 2022-09-19 12:19:31 +08:00
徐涛
ed83fbc77c fix(fee):完成配电维护费部分的查询语句调整。 2022-09-19 11:54:23 +08:00
徐涛
308c9e959c fix(park):修正一处应用错误。 2022-09-19 11:27:37 +08:00
徐涛
1e04922cee fix(park):完成园区部分的数据库迁移调整。 2022-09-19 11:24:18 +08:00
徐涛
a9f93d5239 enhance(model):调整自定义的日期类型,以及相关的查询。 2022-09-19 11:06:10 +08:00
徐涛
d885538500 build(debug):增加允许在VSCode中使用jsoniter进行编译的功能。 2022-09-19 08:52:25 +08:00
徐涛
f13ba3fca0 enhance(model):增加一个专门用于保存日期的自定义类型。 2022-09-19 08:51:55 +08:00
徐涛
720bdd54fe fix(user):修正转供电企业快速查询功能。 2022-09-18 22:24:09 +08:00
徐涛
b638bc5f75 fix(charge):修正日期保存时的时区问题。 2022-09-18 22:17:00 +08:00
徐涛
3f8ced5453 doc(readme):更新项目编写说明。 2022-09-18 22:15:40 +08:00
徐涛
ecd2238c9e fix(user):修正指定用户详细信息的获取。 2022-09-18 11:45:03 +08:00
徐涛
2218e27838 fix(user):修正对于用户名称是否为空的判断。 2022-09-18 11:42:34 +08:00
徐涛
710d285733 fix(user):更新用户名称在修改的时候,拼音缩写不跟随修改的问题。 2022-09-18 11:40:40 +08:00
徐涛
7b8ee5ddbd fix(model):补充所有数据模型中用于记录操作时间的Hook。 2022-09-18 11:31:32 +08:00
徐涛
e40ba55825 fix(user):修正检索用户时语句中的引用问题。 2022-09-18 10:50:49 +08:00
徐涛
2aa6939186 fix(stat):修正统计部分的关联设计。 2022-09-18 10:26:43 +08:00
徐涛
1c0ebc9559 fix(region):修正行政区划中的查询错误。 2022-09-18 10:15:53 +08:00
徐涛
0d062560da fix(model):修正模型定义中的继承部分。 2022-09-18 10:11:23 +08:00
徐涛
9bbb58ae55 fix(migrate):修正迁移文件中的错误。 2022-09-18 10:03:32 +08:00
徐涛
d0d6c9f721 refactor(main):基本完成主进程及启动任务的迁移,清理项目依赖。 2022-09-18 09:59:49 +08:00
徐涛
3f60376061 refactor(user):用户控制器基本完成迁移。 2022-09-18 08:18:31 +08:00
徐涛
cea90c5b29 fix(query):修正查询时所使用的变量类型。 2022-09-18 07:07:26 +08:00
徐涛
ffaccc4c88 fix(service):修正一些使用错误。 2022-09-17 21:35:50 +08:00
徐涛
60d2db6cdd refactor(calculate):报表计算服务基本完成迁移。 2022-09-17 21:31:37 +08:00
徐涛
805911f72b refactor(stat):统计服务基本完成迁移。 2022-09-17 18:45:02 +08:00
徐涛
df08c31278 refactor(god):天神模式基本完成迁移。 2022-09-17 18:26:23 +08:00
徐涛
fc3f931362 refactor(enduser):终端用户抄表控制基本完成迁移。 2022-09-17 07:05:17 +08:00
徐涛
e250ef6792 refactor(withdraw):报表撤回系列功能基本完成迁移。 2022-09-16 22:17:19 +08:00
徐涛
d778c4d3f3 refactor(report):报表的服务功能基本完成迁移。 2022-09-16 17:27:10 +08:00
徐涛
7ce9abe1de enhance(model):增加两个用于快速转换数据结构的方法。 2022-09-16 17:26:41 +08:00
徐涛
dba74ecd49 fix(fee):修正bun中Model方法传递的内容。 2022-09-16 13:09:07 +08:00
徐涛
398e67a7bd enhance(fee):为园区固定费用服务增加调试用的日志记录器。 2022-09-16 10:38:08 +08:00
徐涛
ab7ce6d0c6 refactor(fee):园区固定费用基本完成迁移。 2022-09-16 10:36:14 +08:00
徐涛
e2767501fb refactor(meter):园区终端用户表计基本完成迁移。 2022-09-16 10:07:15 +08:00
徐涛
3d20ceb35a refactor(park):园区部分基本完成迁移。 2022-09-16 09:26:48 +08:00
徐涛
c6c1423364 refactor(charge):基本完成用户计费部分的迁移。 2022-09-16 08:51:10 +08:00
徐涛
cb2908435a refactor(context):改进超时上下文的生成。 2022-09-16 05:38:44 +08:00
徐涛
92e8d312dd enhance(migrate):用户费用记录改为自增长序列类型。 2022-09-16 05:28:37 +08:00
徐涛
ce7b69923d refactor(user):基本完成用户服务层的迁移。 2022-09-15 21:03:05 +08:00
徐涛
f8025c5dea fix(region):纠正一个可能错误的Where子句用法。 2022-09-15 16:29:39 +08:00
徐涛
8687b462ff refactor(region):基本完成行政区划部分服务的迁移。 2022-09-15 16:22:24 +08:00
徐涛
b262042244 feat(global):全局增加一个用于生成带有超时控制的上下文生成器。 2022-09-15 16:14:30 +08:00
徐涛
ae6e6490b2 refactor(model):基本完成对于数据实体模型的迁移。 2022-09-15 15:58:01 +08:00
徐涛
9380879ab5 enhance(migrate):增加基于bun的迁移脚本。 2022-09-15 13:48:46 +08:00
徐涛
4254d020b9 refactor(db):切换数据库驱动为pq,数据控制库为bun,更改Redis连接的名称。 2022-09-15 10:11:27 +08:00
徐涛
769882dce5 fix(dilute):修正配电维护费项目ID的名称,以使其符合API文档的要求。 2022-09-15 09:42:56 +08:00
徐涛
a3a00d162a chore(project):增加版本库文件屏蔽,加入运行配置。 2022-09-15 09:05:38 +08:00
徐涛
a8430e012f enhance(enduser):导出抄表记录模板中携带有已经保存在系统中的抄表数据。 2022-09-13 16:00:41 +08:00
徐涛
85b9890168 fix(enduser):修正峰谷终端用户数据保存时因为文字错误不能保存的问题。 2022-09-09 15:40:35 +08:00
徐涛
e3f6af886b feat(god):增加重新同步终端用户档案的功能。 2022-09-09 15:13:43 +08:00
徐涛
ac70adf92a fix(calculate):报表的计算会根据园区的不同选择不同的计算方式。 2022-09-09 12:28:40 +08:00
徐涛
e1463de00d build(dep):整理依赖。 2022-09-09 10:56:54 +08:00
徐涛
ca5d306b88 enhance(log):优化日志记录器的命名策略。 2022-09-09 10:55:26 +08:00
徐涛
0bd6c27ab9 enhance(log):为默认的日志记录器提供默认的名称。 2022-09-09 10:47:25 +08:00
徐涛
5c0c77202c enhance(log):Web框架所使用的日志中间件改用第三方定义的中间件。 2022-09-09 09:31:51 +08:00
徐涛
54b680f757 enhance(log):日志文件与控制台日志的配置分离。 2022-09-09 09:21:19 +08:00
徐涛
146ef4b7ef enhance(log):改进日志的滚动保存配置。 2022-09-09 08:49:53 +08:00
徐涛
e619d27559 enhance(enduser):为导出的用户抄表记录文件增加文件名内容。 2022-09-08 20:34:01 +08:00
徐涛
dce83d7e49 fix(excel):修复NullDecimal类型内容的导入。 2022-09-08 19:39:10 +08:00
徐涛
6d28740a51 enhance(docker):容器化配置增加日志保存目录卷。 2022-09-08 16:16:02 +08:00
徐涛
8d190a3478 fix(log):清理系统中无用的调试日志输出。 2022-09-08 16:13:45 +08:00
徐涛
46494dd46e feat(log):日志系统切换至zap。 2022-09-08 16:10:08 +08:00
徐涛
d38b6ab064 feat(log):完全使用Zerolog接管项目中的日志记录。 2022-09-08 14:33:54 +08:00
徐涛
4f11249b94 feat(log):加入zerolog和lumberjack的支持,实现日志功能的初步封装。 2022-09-08 13:15:51 +08:00
徐涛
c433652326 fix(park):修复不能正确记录园区峰谷用户表计类型的问题。 2022-09-08 10:59:09 +08:00
徐涛
62a9fec43f enhance(god):清除报表中不同部分的时候同时重置报表的进度状态。 2022-09-07 14:04:52 +08:00
徐涛
466d21e8b4 fix(god):修复并调整SQL语句。 2022-09-07 13:15:19 +08:00
徐涛
ab92f22c85 fix(god):修复删除报表中SQL语句的错误。 2022-09-06 19:03:07 +08:00
徐涛
2e8bbf2b99 fix(god):补充遗漏的抄表记录字段。 2022-09-06 18:52:25 +08:00
徐涛
c3324128d0 fix(god):修复清空报表中维护费方法。 2022-09-06 18:40:18 +08:00
徐涛
7be45d3ffc feat(god):完成天神模式的控制器,整体功能待测。 2022-09-06 17:29:33 +08:00
徐涛
86de5fd3ad enhance(god):附加清理用户计费信息缓存。 2022-09-06 16:51:09 +08:00
徐涛
b0c4984b21 enhance(god):增加几个漏掉删除的数据表。 2022-09-06 16:50:09 +08:00
徐涛
97bad80784 enhance(god):增加清理关联缓存。 2022-09-06 16:46:14 +08:00
徐涛
0ec7f5fba1 Merge branch 'master' into god-mode 2022-09-06 16:08:45 +08:00
徐涛
0d2b1431b6 fix(cache):修复缓存关联关系被不正常清除的问题。 2022-09-06 15:53:24 +08:00
徐涛
8aa4e38760 fix(park):修复园区不能搜索的问题。 2022-09-06 15:28:27 +08:00
徐涛
954285e426 fix(cache):清除已经不用的调试日志。 2022-09-06 15:19:32 +08:00
徐涛
a4a9938675 enhance(cache):精简缓存关系清理的逻辑。 2022-09-06 15:12:32 +08:00
徐涛
8a21a2f469 fix(cache):修正缓存无法保存纯数字内容的问题。 2022-09-06 15:06:34 +08:00
徐涛
a1e9167cdf enhance(cache):增加缓存关联关系中无效键清理方法。 2022-09-06 13:56:30 +08:00
徐涛
2a07db75c7 enhance(cache):删除已经不再使用的缓存方法。 2022-09-06 13:17:59 +08:00
徐涛
60280d0e06 enhance(cache):实体的存在性检查改为使用普通Key存储。 2022-09-06 12:43:04 +08:00
徐涛
f0c22db31f fix(cache):修正缓存关联记录中记录查询数量的键类型。 2022-09-06 12:37:20 +08:00
徐涛
2ea8443409 enhance(cache):改用普通Key记录查询数量。 2022-09-06 12:35:53 +08:00
徐涛
bd1033ac47 enhance(cache):为缓存增加60秒内的随机秒数,以避免缓存雪崩。 2022-09-06 12:28:06 +08:00
徐涛
ae056f612a fix(calculate):修正园区总电量在保存后电度电费不显示的问题。 2022-09-06 11:24:39 +08:00
徐涛
a512773f11 fix(park):修复创建和修改园区的时候园区住户数量不能被保存的问题。 2022-09-06 11:13:34 +08:00
徐涛
901cbf23bb feat(god):基本完成天神模式所使用的数据服务。 2022-09-06 10:45:20 +08:00
徐涛
c4ab500235 fix(calculate):修正终端用户的损失分摊的计算。 2022-09-05 17:57:40 +08:00
徐涛
5332fc9b4f fix(enduser):修正抄表时,平段部分的计算。 2022-09-03 22:03:58 +08:00
徐涛
37971f6875 fix(report):修复配电物业费部分的计算。 2022-09-03 21:39:02 +08:00
徐涛
008ebcee79 fix(report):更改两处计算错误。 2022-09-03 15:49:59 +08:00
徐涛
41c4dcea2e fix(report):补充计算功能传递的数据项。 2022-09-03 09:36:05 +08:00
徐涛
13368eace9 fix(enduser):修正一处逻辑错误。 2022-09-02 19:56:30 +08:00
徐涛
b162844159 enhance(cache):为常用的缓存加入生命期设置。 2022-09-02 19:50:40 +08:00
徐涛
c9f8235339 enhance(enduser):终端用户抄表时可根据当前数据库中记录的数量决定是否读取上传表格中的上期表底数。 2022-09-02 19:43:26 +08:00
徐涛
e1d55e4fc7 enhance(excel):调整Excel分析,以避免因为单元格没有填写导致的panic。 2022-09-02 09:52:45 +08:00
徐涛
17bde54c7c fix(user):更正表示用户登录错误的错误码。 2022-09-02 09:52:02 +08:00
徐涛
7abf35ca97 fix(enduser):修正终端用户表计抄表过程中的错误。 2022-08-30 11:42:15 +08:00
徐涛
e150a22174 fix(report):修正保存报表概览信息的时候无法获取到报表ID的问题。 2022-08-30 11:35:23 +08:00
徐涛
e9a122fcda fix(report):修正对于园区的最新报表期数的检测。 2022-08-30 10:37:11 +08:00
徐涛
b09b3f4f2d fix(fee):修正未能正确获得要更新的维护费ID的问题。 2022-08-30 10:27:36 +08:00
徐涛
50c9195797 fix(fee):修正对于维护费归属的判断语句。 2022-08-30 10:15:18 +08:00
徐涛
bdddcec922 fix(meter):增加表计的默认排序。 2022-08-30 10:14:40 +08:00
83 changed files with 5637 additions and 3105 deletions

2
.gitignore vendored
View File

@@ -179,3 +179,5 @@ fabric.properties
# Block sensitive configuration files # Block sensitive configuration files
settings.local.yaml settings.local.yaml
log/
__debug_bin

19
.vscode/launch.json vendored Normal file
View 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
View 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"
}
]
}

View File

@@ -21,6 +21,7 @@ RUN apk update \
ENV TZ=Asia/Shanghai ENV TZ=Asia/Shanghai
RUN mkdir /app RUN mkdir /app
WORKDIR /app WORKDIR /app
VOLUME ["/app/log"]
COPY --from=builder /app/server . COPY --from=builder /app/server .
COPY settings.yaml . COPY settings.yaml .
COPY regions.csv . COPY regions.csv .

View File

@@ -11,3 +11,18 @@
项目详细设计方案见[详细设计方案](https://kdocs.cn/l/cawe22YUV3bJ),该设计方案未经许可,禁止私自修改。 项目详细设计方案见[详细设计方案](https://kdocs.cn/l/cawe22YUV3bJ),该设计方案未经许可,禁止私自修改。
项目任务分配与状态概览表见[任务概况](https://kdocs.cn/l/camrXvBMlCNs)。 项目任务分配与状态概览表见[任务概况](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.

32
cache/abstract.go vendored
View File

@@ -3,6 +3,7 @@ package cache
import ( import (
"electricity_bill_calc/global" "electricity_bill_calc/global"
"fmt" "fmt"
"math/rand"
"strings" "strings"
"time" "time"
@@ -23,24 +24,25 @@ const (
func Cache[T interface{}](key string, value *T, expires time.Duration) error { func Cache[T interface{}](key string, value *T, expires time.Duration) error {
var err error var err error
if expires > 0 { 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)). Key(key).Value(rueidis.JSON(value)).
ExSeconds(int64(expires.Seconds())). ExSeconds(int64(realExpires.Seconds())).
Build() Build()
err = global.RedisConn.Do(global.Ctx, setCmd).Error() err = global.Rd.Do(global.Ctx, setCmd).Error()
} else { } else {
setCmd := global.RedisConn.B().Set(). setCmd := global.Rd.B().Set().
Key(key).Value(rueidis.JSON(value)). Key(key).Value(rueidis.JSON(value)).
Build() Build()
err = global.RedisConn.Do(global.Ctx, setCmd).Error() err = global.Rd.Do(global.Ctx, setCmd).Error()
} }
return err return err
} }
// 从Redis缓存中获取一个数据 // 从Redis缓存中获取一个数据
func Retreive[T interface{}](key string) (*T, error) { func Retreive[T interface{}](key string) (*T, error) {
getCmd := global.RedisConn.B().Get().Key(key).Build() getCmd := global.Rd.B().Get().Key(key).Build()
result := global.RedisConn.Do(global.Ctx, getCmd) result := global.Rd.Do(global.Ctx, getCmd)
if result.Error() != nil { if result.Error() != nil {
if rueidis.IsRedisNil(result.Error()) { if rueidis.IsRedisNil(result.Error()) {
return nil, nil return nil, nil
@@ -58,8 +60,8 @@ func Retreive[T interface{}](key string) (*T, error) {
// 检查Redis缓存中是否存在指定键的记录 // 检查Redis缓存中是否存在指定键的记录
func Exists(key string) (bool, error) { func Exists(key string) (bool, error) {
existsCmd := global.RedisConn.B().Exists().Key(key).Build() existsCmd := global.Rd.B().Exists().Key(key).Build()
result := global.RedisConn.Do(global.Ctx, existsCmd) result := global.Rd.Do(global.Ctx, existsCmd)
if result.Error() != nil { if result.Error() != nil {
return false, result.Error() return false, result.Error()
} }
@@ -70,8 +72,8 @@ func Exists(key string) (bool, error) {
// 从Redis缓存中删除指定键 // 从Redis缓存中删除指定键
// ! 如果指定键已不存在那么本函数一样会返回false // ! 如果指定键已不存在那么本函数一样会返回false
func Delete(key string) (bool, error) { func Delete(key string) (bool, error) {
deleteCmd := global.RedisConn.B().Del().Key(key).Build() deleteCmd := global.Rd.B().Del().Key(key).Build()
result := global.RedisConn.Do(global.Ctx, deleteCmd) result := global.Rd.Do(global.Ctx, deleteCmd)
if result.Error() != nil { if result.Error() != nil {
return false, result.Error() return false, result.Error()
} }
@@ -109,8 +111,8 @@ func DeleteAll(pattern string) error {
sKeys []string sKeys []string
) )
for { for {
scanCmd := global.RedisConn.B().Scan().Cursor(cursor).Match(pattern).Count(20).Build() scanCmd := global.Rd.B().Scan().Cursor(cursor).Match(pattern).Count(20).Build()
results := global.RedisConn.Do(global.Ctx, scanCmd) results := global.Rd.Do(global.Ctx, scanCmd)
cursor, sKeys, err = dissembleScan(results) cursor, sKeys, err = dissembleScan(results)
if err != nil { if err != nil {
return err return err
@@ -121,8 +123,8 @@ func DeleteAll(pattern string) error {
} }
} }
delCmd := global.RedisConn.B().Del().Key(keys...).Build() delCmd := global.Rd.B().Del().Key(keys...).Build()
err = global.RedisConn.Do(global.Ctx, delCmd).Error() err = global.Rd.Do(global.Ctx, delCmd).Error()
return err return err
} }

55
cache/count.go vendored
View File

@@ -1,49 +1,58 @@
package cache package cache
import ( import (
"electricity_bill_calc/global" "fmt"
"strconv"
"strings" "strings"
"time"
) )
func assembleCountKey(entityName string) string { type _CountRecord struct {
var keys = make([]string, 0) Count int64
keys = append(keys, strings.ToUpper(entityName))
return CacheKey(TAG_COUNT, keys...)
} }
func assembleCountIdentification(additional ...string) string { func assembleCountKey(entityName string, additional ...string) string {
return strings.Join(additional, ":") 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 { func CacheCount(relationNames []string, entityName string, count int64, conditions ...string) error {
countKey := assembleCountKey(entityName) countKey := assembleCountKey(entityName, conditions...)
identification := assembleCountIdentification(conditions...) cacheInstance := &_CountRecord{Count: count}
cmd := global.RedisConn.B().Hset().Key(countKey).FieldValue().FieldValue(identification, strconv.FormatInt(count, 10)).Build() err := Cache(countKey, cacheInstance, 5*time.Minute)
result := global.RedisConn.Do(global.Ctx, cmd)
for _, relationName := range relationNames { 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) { func RetreiveCount(entityName string, condtions ...string) (int64, error) {
countKey := assembleCountKey(entityName) countKey := assembleCountKey(entityName, condtions...)
identification := assembleCountIdentification(condtions...) exist, err := Exists(countKey)
cmd := global.RedisConn.B().Hget().Key(countKey).Field(identification).Build()
result, err := global.RedisConn.Do(global.Ctx, cmd).AsInt64()
if err != nil { if err != nil {
return -1, err return -1, err
} }
return result, nil if !exist {
return -1, nil
}
instance, err := Retreive[_CountRecord](countKey)
if instance != nil && err == nil {
return instance.Count, nil
} else {
return -1, err
}
} }
// 删除指定模型名称的数量缓存 // 删除指定模型名称的数量缓存
func AbolishCountEntity(entityName string) error { func AbolishCountEntity(entityName string) error {
countKey := assembleCountKey(entityName) pattern := fmt.Sprintf("%s:%s:*", TAG_COUNT, strings.ToUpper(entityName))
cmd := global.RedisConn.B().Del().Key(countKey).Build() return DeleteAll(pattern)
err := global.RedisConn.Do(global.Ctx, cmd).Error()
return err
} }

3
cache/entity.go vendored
View File

@@ -3,6 +3,7 @@ package cache
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
) )
func assembleEntityKey(entityName, id string) string { func assembleEntityKey(entityName, id string) string {
@@ -19,7 +20,7 @@ func assembleEntityKey(entityName, id string) string {
// 缓存模型名称明确的使用ID进行检索的实体内容。 // 缓存模型名称明确的使用ID进行检索的实体内容。
func CacheEntity[T any](instance T, relationNames []string, entityName, id string) error { func CacheEntity[T any](instance T, relationNames []string, entityName, id string) error {
entityKey := assembleEntityKey(entityName, id) entityKey := assembleEntityKey(entityName, id)
err := Cache(entityKey, &instance, -1) err := Cache(entityKey, &instance, 5*time.Minute)
for _, relationName := range relationNames { for _, relationName := range relationNames {
CacheRelation(relationName, STORE_TYPE_KEY, entityKey) CacheRelation(relationName, STORE_TYPE_KEY, entityKey)
} }

63
cache/exists.go vendored
View File

@@ -1,72 +1,49 @@
package cache package cache
import ( import (
"electricity_bill_calc/global"
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/samber/lo"
) )
func assembleExistsKey(entityName string) string { func assembleExistsKey(entityName string, additional ...string) string {
var keys = make([]string, 0) var keys = make([]string, 0)
keys = append(keys, strings.ToUpper(entityName)) keys = append(keys, strings.ToUpper(entityName))
return CacheKey(TAG_EXISTS, keys...) keys = append(keys, additional...)
} var b strings.Builder
b.WriteString(TAG_EXISTS)
func assembleExistsIdentification(additional ...string) string { for _, s := range keys {
return strings.Join(additional, ":") fmt.Fprintf(&b, ":%s", s)
}
return b.String()
} }
// 缓存模型名称明确的、包含指定ID以及一些附加条件的记录 // 缓存模型名称明确的、包含指定ID以及一些附加条件的记录
func CacheExists(relationNames []string, entityName string, conditions ...string) error { func CacheExists(relationNames []string, entityName string, conditions ...string) error {
existskey := assembleExistsKey(entityName) existskey := assembleExistsKey(entityName, conditions...)
identification := assembleExistsIdentification(conditions...) err := Cache(existskey, lo.ToPtr(true), 5*time.Minute)
cmd := global.RedisConn.B().Sadd().Key(existskey).Member(identification).Build()
err := global.RedisConn.Do(global.Ctx, cmd).Error()
for _, relationName := range relationNames { for _, relationName := range relationNames {
CacheRelation(relationName, STORE_TYPE_SET, existskey, identification) CacheRelation(relationName, STORE_TYPE_KEY, existskey)
} }
return err return err
} }
// 从缓存中获取模型名称明确、包含指定ID以及一些附加条件的实体是否存在的标记函数在返回false时不保证数据库中相关记录也不存在 // 从缓存中获取模型名称明确、包含指定ID以及一些附加条件的实体是否存在的标记函数在返回false时不保证数据库中相关记录也不存在
func CheckExists(entityName string, condtions ...string) (bool, error) { func CheckExists(entityName string, condtions ...string) (bool, error) {
existsKey := assembleExistsKey(entityName) existsKey := assembleExistsKey(entityName, condtions...)
identification := assembleExistsIdentification(condtions...) return Exists(existsKey)
cmd := global.RedisConn.B().Sismember().Key(existsKey).Member(identification).Build()
result, err := global.RedisConn.Do(global.Ctx, cmd).AsBool()
return result, err
} }
// 从缓存中删除模型名称明确、包含指定ID的全部实体存在标记 // 从缓存中删除模型名称明确、包含指定ID的全部实体存在标记
func AbolishExists(entityName, id string) error { func AbolishExists(entityName, id string) error {
existsKey := assembleExistsKey(entityName) pattern := fmt.Sprintf("%s:%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName), id)
pattern := fmt.Sprintf("%s*", id) return DeleteAll(pattern)
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
} }
// 从缓存中删除指定模型名称的全部存在标记 // 从缓存中删除指定模型名称的全部存在标记
func AbolishExistsEntity(entityName string) error { func AbolishExistsEntity(entityName string) error {
existskey := assembleExistsKey(entityName) pattern := fmt.Sprintf("%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName))
_, err := Delete(existskey) return DeleteAll(pattern)
return err
} }

67
cache/relation.go vendored
View File

@@ -2,6 +2,7 @@ package cache
import ( import (
"electricity_bill_calc/global" "electricity_bill_calc/global"
"fmt"
"strings" "strings"
"github.com/rueian/rueidis" "github.com/rueian/rueidis"
@@ -31,16 +32,16 @@ func assembleRelationIdentity(storeType, key string, field ...string) string {
func CacheRelation(relationName, storeType, key string, field ...string) error { func CacheRelation(relationName, storeType, key string, field ...string) error {
relationKey := assembleRelationKey(relationName) relationKey := assembleRelationKey(relationName)
relationIdentity := assembleRelationIdentity(storeType, key, field...) relationIdentity := assembleRelationIdentity(storeType, key, field...)
cmd := global.RedisConn.B().Sadd().Key(relationKey).Member(relationIdentity).Build() cmd := global.Rd.B().Sadd().Key(relationKey).Member(relationIdentity).Build()
result := global.RedisConn.Do(global.Ctx, cmd) result := global.Rd.Do(global.Ctx, cmd)
return result.Error() return result.Error()
} }
// 从缓存中清理指定的关联键 // 从缓存中清理指定的关联键
func AbolishRelation(relationName string) error { func AbolishRelation(relationName string) error {
relationKey := assembleRelationKey(relationName) relationKey := assembleRelationKey(relationName)
cmd := global.RedisConn.B().Smembers().Key(relationKey).Build() cmd := global.Rd.B().Smembers().Key(relationKey).Build()
relationItems, err := global.RedisConn.Do(global.Ctx, cmd).AsStrSlice() relationItems, err := global.Rd.Do(global.Ctx, cmd).AsStrSlice()
if err != nil { if err != nil {
return err return err
} }
@@ -49,17 +50,67 @@ func AbolishRelation(relationName string) error {
separated := strings.Split(item, ";") separated := strings.Split(item, ";")
switch separated[0] { switch separated[0] {
case STORE_TYPE_KEY: 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) cmds = append(cmds, cmd)
case STORE_TYPE_HASH: 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) cmds = append(cmds, cmd)
case STORE_TYPE_SET: 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) 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 int64
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 { firstErr, has := lo.Find(errs, func(elem rueidis.RedisResult) bool {
return elem.Error() != nil return elem.Error() != nil
}) })

33
cache/repository.go vendored
View File

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

3
cache/search.go vendored
View File

@@ -3,6 +3,7 @@ package cache
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
) )
func assembleSearchKey(entityName string, additional ...string) string { func assembleSearchKey(entityName string, additional ...string) string {
@@ -20,7 +21,7 @@ func assembleSearchKey(entityName string, additional ...string) string {
// 缓存模型名称明确的使用或者包含非ID检索条件的实体内容。 // 缓存模型名称明确的使用或者包含非ID检索条件的实体内容。
func CacheSearch[T any](instance T, relationNames []string, entityName string, conditions ...string) error { func CacheSearch[T any](instance T, relationNames []string, entityName string, conditions ...string) error {
searchKey := assembleSearchKey(entityName, conditions...) searchKey := assembleSearchKey(entityName, conditions...)
err := Cache(searchKey, &instance, -1) err := Cache(searchKey, &instance, 5*time.Minute)
for _, relationName := range relationNames { for _, relationName := range relationNames {
CacheRelation(relationName, STORE_TYPE_KEY, searchKey) CacheRelation(relationName, STORE_TYPE_KEY, searchKey)
} }

View File

@@ -4,12 +4,12 @@ import (
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
func _retreiveSession(c *gin.Context) (*model.Session, error) { func _retreiveSession(c *fiber.Ctx) (*model.Session, error) {
session, exists := c.Get("session") session := c.Locals("session")
if !exists { if session == nil {
return nil, exceptions.NewUnauthorizedError("用户会话不存在") return nil, exceptions.NewUnauthorizedError("用户会话不存在")
} }
userSession, ok := session.(*model.Session) userSession, ok := session.(*model.Session)

View File

@@ -9,35 +9,33 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeChargesController(router *gin.Engine) { func InitializeChargesController(app *fiber.App) {
router.GET("/charges", security.OPSAuthorize, listAllCharges) app.Get("/charges", security.OPSAuthorize, listAllCharges)
router.POST("/charge", security.OPSAuthorize, recordNewCharge) app.Post("/charge", security.OPSAuthorize, recordNewCharge)
router.PUT("/charge/:uid/:seq", security.OPSAuthorize, modifyChargeState) app.Put("/charge/:uid/:seq", security.OPSAuthorize, modifyChargeState)
} }
func listAllCharges(c *gin.Context) { func listAllCharges(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
requestKeyword := c.DefaultQuery("keyword", "") requestKeyword := c.Query("keyword", "")
requestBeginDate := c.DefaultQuery("begin", "") requestBeginDate := c.Query("begin", "")
requestEndDate := c.DefaultQuery("end", "") requestEndDate := c.Query("end", "")
charges, total, err := service.ChargeService.ListPagedChargeRecord(requestKeyword, requestBeginDate, requestEndDate, requestPage) charges, total, err := service.ChargeService.ListPagedChargeRecord(requestKeyword, requestBeginDate, requestEndDate, requestPage)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json( return result.Json(
http.StatusOK, "已获取到符合条件的计费记录。", http.StatusOK, "已获取到符合条件的计费记录。",
response.NewPagedResponse(requestPage, total).ToMap(), response.NewPagedResponse(requestPage, total).ToMap(),
gin.H{"records": charges}, fiber.Map{"records": charges},
) )
} }
@@ -46,13 +44,15 @@ type _NewChargeFormData struct {
Fee decimal.NullDecimal `json:"fee" form:"fee"` Fee decimal.NullDecimal `json:"fee" form:"fee"`
Discount decimal.NullDecimal `json:"discount" form:"discount"` Discount decimal.NullDecimal `json:"discount" form:"discount"`
Amount decimal.NullDecimal `json:"amount" form:"amount"` Amount decimal.NullDecimal `json:"amount" form:"amount"`
ChargeTo time.Time `json:"chargeTo" form:"chargeTo" time_format:"simple_date" time_location:"shanghai"` ChargeTo model.Date `json:"chargeTo" form:"chargeTo"`
} }
func recordNewCharge(c *gin.Context) { func recordNewCharge(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
formData := new(_NewChargeFormData) formData := new(_NewChargeFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
currentTime := time.Now() currentTime := time.Now()
newRecord := &model.UserCharge{ newRecord := &model.UserCharge{
UserId: formData.UserId, UserId: formData.UserId,
@@ -65,30 +65,29 @@ func recordNewCharge(c *gin.Context) {
} }
err := service.ChargeService.CreateChargeRecord(newRecord, true) err := service.ChargeService.CreateChargeRecord(newRecord, true)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Created("指定用户的服务已延期。") return result.Created("指定用户的服务已延期。")
} }
type _StateChangeFormData struct { type _StateChangeFormData struct {
Cancelled bool `json:"cancelled"` Cancelled bool `json:"cancelled"`
} }
func modifyChargeState(c *gin.Context) { func modifyChargeState(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
formData := new(_StateChangeFormData) formData := new(_StateChangeFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
requestUserID := c.Param("uid") return result.UnableToParse("无法解析提交的数据。")
requestChargeSeq, err := strconv.Atoi(c.Param("seq")) }
if err != nil { requestUserID := c.Params("uid")
result.Error(http.StatusNotAcceptable, "参数[记录流水号]解析错误。") requestChargeSeq, err := strconv.Atoi(c.Params("seq", "-1"))
return if err != nil || requestChargeSeq == -1 {
return result.Error(http.StatusNotAcceptable, "参数[记录流水号]解析错误。")
} }
err = service.ChargeService.CancelCharge(int64(requestChargeSeq), requestUserID) err = service.ChargeService.CancelCharge(int64(requestChargeSeq), requestUserID)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定用户服务延期记录状态已经更新。") return result.Updated("指定用户服务延期记录状态已经更新。")
} }

View File

@@ -1,77 +1,81 @@
package controller package controller
import ( import (
"database/sql"
"electricity_bill_calc/excel" "electricity_bill_calc/excel"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/model"
"electricity_bill_calc/response" "electricity_bill_calc/response"
"electricity_bill_calc/security" "electricity_bill_calc/security"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeEndUserController(router *gin.Engine) { func InitializeEndUserController(router *fiber.App) {
router.GET("/report/:rid/submeter", security.EnterpriseAuthorize, fetchEndUserInReport) router.Get("/report/:rid/submeter", security.EnterpriseAuthorize, fetchEndUserInReport)
router.GET("/report/:rid/meter/template", security.EnterpriseAuthorize, downloadEndUserRegisterTemplate) router.Get("/report/:rid/meter/template", downloadEndUserRegisterTemplate)
router.POST("/report/:rid/meter/batch", security.EnterpriseAuthorize, uploadEndUserRegisterTemplate) router.Post("/report/:rid/meter/batch", security.EnterpriseAuthorize, uploadEndUserRegisterTemplate)
router.PUT("/report/:rid/submeter/:pid/:mid", security.EnterpriseAuthorize, modifyEndUserRegisterRecord) router.Put("/report/:rid/submeter/:pid/:mid", security.EnterpriseAuthorize, modifyEndUserRegisterRecord)
router.Get("/end/user/adjusts", security.MustAuthenticated, statEndUserInPeriod)
} }
func fetchEndUserInReport(c *gin.Context) { func fetchEndUserInReport(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
keyword := c.DefaultQuery("keyword", "") keyword := c.Query("keyword")
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
endUsers, totalItem, err := service.EndUserService.SearchEndUserRecord(requestReportId, keyword, requestPage) endUsers, totalItem, err := service.EndUserService.SearchEndUserRecord(requestReportId, keyword, requestPage)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json( return result.Json(
http.StatusOK, http.StatusOK,
"已获取到符合条件的终端用户集合", "已获取到符合条件的终端用户集合",
response.NewPagedResponse(requestPage, totalItem).ToMap(), response.NewPagedResponse(requestPage, totalItem).ToMap(),
gin.H{"meters": endUsers}, fiber.Map{"meters": endUsers},
) )
} }
func downloadEndUserRegisterTemplate(c *gin.Context) { func downloadEndUserRegisterTemplate(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) {
return
}
users, err := service.EndUserService.AllEndUserRecord(requestReportId) users, err := service.EndUserService.AllEndUserRecord(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return }
reportIndex, err := service.ReportService.RetreiveReportIndex(requestReportId)
if err != nil {
return result.NotFound(err.Error())
}
park, err := service.ParkService.FetchParkDetail(reportIndex.ParkId)
if err != nil {
return result.NotFound(err.Error())
} }
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if meterType == -1 { if meterType == -1 {
result.NotFound("未能确定用户表计类型。") return result.NotFound("未能确定用户表计类型。")
return
} }
c.Status(http.StatusOK) c.Status(http.StatusOK)
c.Header("Content-Type", "application/octet-stream") c.Set("Content-Type", "application/octet-stream")
c.Header("Content-Transfer-Encoding", "binary") c.Set("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition", "attachment; filename=抄表记录.xlsx") c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=抄表记录-%s-%s.xlsx", park.Name, reportIndex.Period.Format("2006-01")))
gen := lo.Ternary[excel.ExcelTemplateGenerator]( gen := lo.Ternary[excel.ExcelTemplateGenerator](
meterType == 0, meterType == 0,
@@ -80,49 +84,45 @@ func downloadEndUserRegisterTemplate(c *gin.Context) {
) )
defer gen.Close() defer gen.Close()
gen.WriteMeterData(users) gen.WriteMeterData(users)
gen.WriteTo(c.Writer) gen.WriteTo(c.Response().BodyWriter())
return nil
} }
func uploadEndUserRegisterTemplate(c *gin.Context) { func uploadEndUserRegisterTemplate(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if meterType == -1 { if meterType == -1 {
result.NotFound("未能确定用户表计类型。") return result.NotFound("未能确定用户表计类型。")
return
} }
uploadedFile, err := c.FormFile("data") uploadedFile, err := c.FormFile("data")
if err != nil { if err != nil {
result.NotAccept("没有接收到上传的档案文件。") return result.NotAccept("没有接收到上传的档案文件。")
return
} }
archiveFile, err := uploadedFile.Open() archiveFile, err := uploadedFile.Open()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if meterType == 0 { if meterType == 0 {
errs := service.EndUserService.BatchImportNonPVRegister(requestReportId, archiveFile) errs := service.EndUserService.BatchImportNonPVRegister(requestReportId, archiveFile)
if errs.Len() > 0 { if errs.Len() > 0 {
result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", gin.H{"errors": errs.Errs}) return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs})
return
} }
} else { } else {
errs := service.EndUserService.BatchImportPVRegister(requestReportId, archiveFile) errs := service.EndUserService.BatchImportPVRegister(requestReportId, archiveFile)
if errs.Len() > 0 { if errs.Len() > 0 {
result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", gin.H{"errors": errs.Errs}) return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs})
return
} }
} }
result.Json(http.StatusOK, "已经成功完成抄表记录的导入。", gin.H{"errors": make([]error, 0)}) return result.Json(http.StatusOK, "已经成功完成抄表记录的导入。", fiber.Map{"errors": make([]error, 0)})
} }
type ModifyEndUserRegisterFormData struct { type ModifyEndUserRegisterFormData struct {
@@ -136,29 +136,28 @@ type ModifyEndUserRegisterFormData struct {
AdjustValley decimal.NullDecimal `json:"adjustValley" form:"adjustValley"` AdjustValley decimal.NullDecimal `json:"adjustValley" form:"adjustValley"`
} }
func modifyEndUserRegisterRecord(c *gin.Context) { func modifyEndUserRegisterRecord(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if meterType == -1 { if meterType == -1 {
result.NotFound("未能确定用户表计类型。") return result.NotFound("未能确定用户表计类型。")
return
} }
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
requestMeterId := c.Param("mid") requestMeterId := c.Params("mid")
formData := new(ModifyEndUserRegisterFormData) formData := new(ModifyEndUserRegisterFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
meter, err := service.EndUserService.FetchSpecificEndUserRecord(requestReportId, requestParkId, requestMeterId) meter, err := service.EndUserService.FetchSpecificEndUserRecord(requestReportId, requestParkId, requestMeterId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
if formData.CurrentPeriodOverall.Valid { if formData.CurrentPeriodOverall.Valid {
meter.CurrentPeriodOverall = formData.CurrentPeriodOverall.Decimal meter.CurrentPeriodOverall = formData.CurrentPeriodOverall.Decimal
@@ -186,30 +185,53 @@ func modifyEndUserRegisterRecord(c *gin.Context) {
} }
valid, err := meter.Validate() valid, err := meter.Validate()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if !valid { if !valid {
result.NotAccept("抄表数据合法性验证失败。") return result.NotAccept("抄表数据合法性验证失败。")
return
} }
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
if err = tx.Begin(); err != nil { defer cancel()
result.Error(http.StatusInternalServerError, err.Error()) tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
return if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
} }
defer tx.Close() err = service.EndUserService.UpdateEndUserRegisterRecord(&tx, &ctx, *meter)
err = service.EndUserService.UpdateEndUserRegisterRecord(tx, *meter)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
err = tx.Commit() err = tx.Commit()
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Success("指定终端用户抄表记录已经更新。") return result.Success("指定终端用户抄表记录已经更新。")
}
func statEndUserInPeriod(c *fiber.Ctx) error {
result := response.NewResult(c)
session, err := _retreiveSession(c)
if err != nil {
return result.Unauthorized(err.Error())
}
requestUser := lo.
If(session.Type == model.USER_TYPE_ENT, session.Uid).
Else(c.Query("user"))
requestPark := c.Query("park")
if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT {
if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure {
return err
}
}
startDate := c.Query("start")
endDate := c.Query("end")
stat, err := service.EndUserService.StatEndUserRecordInPeriod(requestUser, requestPark, startDate, endDate)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
return result.Success(
"已经完成终端用户的费用统计",
fiber.Map{"details": stat},
)
} }

176
controller/god_mode.go Normal file
View File

@@ -0,0 +1,176 @@
package controller
import (
"electricity_bill_calc/exceptions"
"electricity_bill_calc/response"
"electricity_bill_calc/security"
"electricity_bill_calc/service"
"net/http"
"github.com/gofiber/fiber/v2"
)
func InitializeGodModeController(router *fiber.App) {
gmR := router.Group("/gm")
{
gmR.Delete("/report/:rid/summary", security.SingularityAuthorize, gmResetReportSummary)
gmR.Delete("/report/:rid/maintenance", security.SingularityAuthorize, gmResetReportMaintenance)
gmR.Delete("/report/:rid/meters", security.SingularityAuthorize, gmResetReportEndUserRecord)
gmR.Post("/report/:rid/meters", security.SingularityAuthorize, gmResynchronizeReportEndUserRecord)
gmR.Delete("/report/:rid", security.SingularityAuthorize, gmResetReport)
gmR.Delete("/report/:rid/force", security.SingularityAuthorize, gmDeleteReport)
gmR.Delete("/park/:pid/maintenance/:mid", security.SingularityAuthorize, gmDeleteSpecificMaintenance)
gmR.Delete("/park/:pid/maintenance", security.SingularityAuthorize, gmDeleteAllMaintenance)
gmR.Delete("/park/:pid/meters", security.SingularityAuthorize, gmDeleteAllMeters)
gmR.Delete("/park/:pid/force", security.SingularityAuthorize, gmDeletePark)
gmR.Delete("/enterprise/:uid/force", security.SingularityAuthorize, gmDeleteUser)
}
}
func gmResetReportSummary(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.ClearReportSummary(requestReportId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的园区总览部分。")
}
return result.Success("指定报表的园区总览已经重置。")
}
func gmResetReportMaintenance(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.ClearReportMaintenances(requestReportId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的配电维护费部分。")
}
return result.Success("指定报表的配电维护费已经重置。")
}
func gmResynchronizeReportEndUserRecord(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.ResynchronizeEndUser(requestReportId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录基本档案。")
}
return result.Success("指定报表的抄表记录基本档案已经重新同步。")
}
func gmResetReportEndUserRecord(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.ResetEndUserRegisterRecords(requestReportId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录部分。")
}
return result.Success("指定报表的抄表记录已经重置。")
}
func gmResetReport(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.ResetReport(requestReportId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功重置指定报表。")
}
return result.Success("指定报表已经重置。")
}
func gmDeleteReport(c *fiber.Ctx) error {
result := response.NewResult(c)
requestReportId := c.Params("rid")
done, err := service.GodModeService.DeleteReport(requestReportId)
if err != nil {
if ipErr, ok := err.(exceptions.ImproperOperateError); ok {
return result.NotAccept(ipErr.Message)
} else {
return result.Error(http.StatusInternalServerError, err.Error())
}
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除指定报表。")
}
return result.Success("指定报表已经删除。")
}
func gmDeleteSpecificMaintenance(c *fiber.Ctx) error {
result := response.NewResult(c)
requestParkId := c.Params("pid")
requestMaintenanceId := c.Params("mid")
done, err := service.GodModeService.RemoveSpecificMaintenance(requestParkId, requestMaintenanceId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除指定的维护费用记录。")
}
return result.Success("指定维护费用记录已经删除。")
}
func gmDeleteAllMaintenance(c *fiber.Ctx) error {
result := response.NewResult(c)
requestParkId := c.Params("pid")
done, err := service.GodModeService.RemoveAllMaintenance(requestParkId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除全部维护费用记录。")
}
return result.Success("全部维护费用记录已经删除。")
}
func gmDeleteAllMeters(c *fiber.Ctx) error {
result := response.NewResult(c)
requestParkId := c.Params("pid")
done, err := service.GodModeService.RemoveAllMeters(requestParkId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除全部终端表计档案记录。")
}
return result.Success("全部终端表计档案记录已经删除。")
}
func gmDeletePark(c *fiber.Ctx) error {
result := response.NewResult(c)
requestParkId := c.Params("pid")
done, err := service.GodModeService.RemovePark(requestParkId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除指定的园区。")
}
return result.Success("指定的园区已经删除。")
}
func gmDeleteUser(c *fiber.Ctx) error {
result := response.NewResult(c)
requestUserId := c.Params("uid")
done, err := service.GodModeService.DeleteUser(requestUserId)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
if !done {
return result.Error(http.StatusInternalServerError, "未能成功删除指定的用户。")
}
return result.Success("指定的用户及其关联信息已经删除。")
}

View File

@@ -6,93 +6,108 @@ import (
"electricity_bill_calc/security" "electricity_bill_calc/security"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeMaintenanceFeeController(router *gin.Engine) { func InitializeMaintenanceFeeController(router *fiber.App) {
router.GET("/maintenance/fee", security.EnterpriseAuthorize, listMaintenanceFees) router.Get("/maintenance/fee", security.MustAuthenticated, listMaintenanceFees)
router.POST("/maintenance/fee", security.EnterpriseAuthorize, createMaintenanceFeeRecord) router.Post("/maintenance/fee", security.EnterpriseAuthorize, createMaintenanceFeeRecord)
router.PUT("/maintenance/fee/:mid", security.EnterpriseAuthorize, modifyMaintenanceFeeRecord) router.Put("/maintenance/fee/:mid", security.EnterpriseAuthorize, modifyMaintenanceFeeRecord)
router.PUT("/maintenance/fee/:mid/enabled", security.EnterpriseAuthorize, changeMaintenanceFeeState) router.Put("/maintenance/fee/:mid/enabled", security.EnterpriseAuthorize, changeMaintenanceFeeState)
router.DELETE("/maintenance/fee/:mid", security.EnterpriseAuthorize, deleteMaintenanceFee) router.Delete("/maintenance/fee/:mid", security.EnterpriseAuthorize, deleteMaintenanceFee)
router.Get("/additional/charges", security.MustAuthenticated, statAdditionalCharges)
} }
func ensureMaintenanceFeeBelongs(c *gin.Context, result *response.Result, requestMaintenanceFeeId string) bool { func ensureMaintenanceFeeBelongs(c *fiber.Ctx, result *response.Result, requestMaintenanceFeeId string) (bool, error) {
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return false, result.Unauthorized(err.Error())
return false
} }
sure, err := service.MaintenanceFeeService.EnsureFeeBelongs(userSession.Uid, requestMaintenanceFeeId) sure, err := service.MaintenanceFeeService.EnsureFeeBelongs(userSession.Uid, requestMaintenanceFeeId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return false, result.Error(http.StatusInternalServerError, err.Error())
return false
} }
if !sure { if !sure {
result.Unauthorized("所操作维护费记录不属于当前用户。") return false, result.Unauthorized("所操作维护费记录不属于当前用户。")
return false
} }
return true return true, nil
} }
func listMaintenanceFees(c *gin.Context) { func listMaintenanceFees(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
requestPark := c.DefaultQuery("park", "") requestPark := c.Query("park")
if len(requestPark) > 0 { requestPeriod := c.Query("period")
if !ensureParkBelongs(c, result, requestPark) { requestPage, err := strconv.Atoi(c.Query("page", "1"))
return
}
fees, err := service.MaintenanceFeeService.ListMaintenanceFees([]string{requestPark})
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, "不能解析给定的参数[page]。")
return
} }
result.Json(http.StatusOK, "已获取指定园区下的维护费记录", gin.H{"fees": fees}) if len(requestPark) > 0 {
if userSession.Type == model.USER_TYPE_ENT {
if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure {
return err
}
}
fees, total, err := service.MaintenanceFeeService.ListMaintenanceFees([]string{requestPark}, requestPeriod, requestPage)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
return result.Json(
http.StatusOK,
"已获取指定园区下的维护费记录",
response.NewPagedResponse(requestPage, total).ToMap(),
fiber.Map{"fees": fees},
)
} else { } else {
parkIds, err := service.ParkService.AllParkIds(userSession.Uid) parkIds, err := service.ParkService.AllParkIds(userSession.Uid)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
fees, err := service.MaintenanceFeeService.ListMaintenanceFees(parkIds) fees, total, err := service.MaintenanceFeeService.ListMaintenanceFees(parkIds, requestPeriod, requestPage)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已获取指定用户下的所有维护费记录。", gin.H{"fees": fees}) return result.Json(
http.StatusOK,
"已获取指定用户下的所有维护费记录。",
response.NewPagedResponse(requestPage, total).ToMap(),
fiber.Map{"fees": fees},
)
} }
} }
type _FeeCreationFormData struct { type _FeeCreationFormData struct {
ParkId string `json:"parkId" form:"parkId"` ParkId string `json:"parkId" form:"parkId"`
Name string `json:"name" form:"name"` Name string `json:"name" form:"name"`
Period string `json:"period" form:"period"`
Fee decimal.Decimal `json:"fee" form:"fee"` Fee decimal.Decimal `json:"fee" form:"fee"`
Memo *string `json:"memo" form:"memo"` Memo *string `json:"memo" form:"memo"`
} }
func createMaintenanceFeeRecord(c *gin.Context) { func createMaintenanceFeeRecord(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
formData := new(_FeeCreationFormData) formData := new(_FeeCreationFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
if !ensureParkBelongs(c, result, formData.ParkId) { return result.UnableToParse("无法解析提交的数据。")
return }
if ensure, err := ensureParkBelongs(c, &result, formData.ParkId); !ensure {
return err
} }
newMaintenanceFee := &model.MaintenanceFee{} newMaintenanceFee := &model.MaintenanceFee{}
copier.Copy(newMaintenanceFee, formData) copier.Copy(newMaintenanceFee, formData)
err := service.MaintenanceFeeService.CreateMaintenanceFeeRecord(*newMaintenanceFee) err := service.MaintenanceFeeService.CreateMaintenanceFeeRecord(*newMaintenanceFee)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Created("新维护费记录已经创建。") return result.Created("新维护费记录已经创建。")
} }
type _FeeModificationFormData struct { type _FeeModificationFormData struct {
@@ -100,54 +115,88 @@ type _FeeModificationFormData struct {
Memo *string `json:"memo" form:"memo"` Memo *string `json:"memo" form:"memo"`
} }
func modifyMaintenanceFeeRecord(c *gin.Context) { func modifyMaintenanceFeeRecord(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestFee := c.Param("mid") requestFee := c.Params("mid")
formData := new(_FeeModificationFormData) formData := new(_FeeModificationFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
if !ensureMaintenanceFeeBelongs(c, result, requestFee) { return result.UnableToParse("无法解析提交的数据。")
return }
if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure {
return err
} }
newFeeState := new(model.MaintenanceFee) newFeeState := new(model.MaintenanceFee)
copier.Copy(newFeeState, formData) copier.Copy(newFeeState, formData)
newFeeState.Id = requestFee
err := service.MaintenanceFeeService.ModifyMaintenanceFee(*newFeeState) err := service.MaintenanceFeeService.ModifyMaintenanceFee(*newFeeState)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定维护费条目已更新。") return result.Updated("指定维护费条目已更新。")
} }
type _FeeStateFormData struct { type _FeeStateFormData struct {
Enabled bool `json:"enabled" form:"enabled"` Enabled bool `json:"enabled" form:"enabled"`
} }
func changeMaintenanceFeeState(c *gin.Context) { func changeMaintenanceFeeState(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestFee := c.Param("mid") requestFee := c.Params("mid")
formData := new(_FeeStateFormData) formData := new(_FeeStateFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
if !ensureMaintenanceFeeBelongs(c, result, requestFee) { return result.UnableToParse("无法解析提交的数据。")
return }
if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure {
return err
} }
err := service.MaintenanceFeeService.ChangeMaintenanceFeeState(requestFee, formData.Enabled) err := service.MaintenanceFeeService.ChangeMaintenanceFeeState(requestFee, formData.Enabled)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定维护费条目状态已更新。") return result.Updated("指定维护费条目状态已更新。")
} }
func deleteMaintenanceFee(c *gin.Context) { func deleteMaintenanceFee(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestFee := c.Param("mid") requestFee := c.Params("mid")
if !ensureMaintenanceFeeBelongs(c, result, requestFee) { if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure {
return return err
} }
err := service.MaintenanceFeeService.DeleteMaintenanceFee(requestFee) err := service.MaintenanceFeeService.DeleteMaintenanceFee(requestFee)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Deleted("指定维护费条目已删除。") return result.Deleted("指定维护费条目已删除。")
}
func statAdditionalCharges(c *fiber.Ctx) error {
result := response.NewResult(c)
session, err := _retreiveSession(c)
if err != nil {
return result.Unauthorized(err.Error())
}
requestUser := lo.
If(session.Type == model.USER_TYPE_ENT, session.Uid).
Else(c.Query("user"))
requestPark := c.Query("park")
if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT {
if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure {
return err
}
}
period := c.Query("period", "")
keyword := c.Query("keyword", "")
requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil {
return result.Error(http.StatusInternalServerError, "不能解析给定的参数[page]。")
}
fees, total, err := service.MaintenanceFeeService.QueryAdditionalCharges(requestUser, requestPark, period, keyword, requestPage)
if err != nil {
return result.Error(http.StatusInternalServerError, err.Error())
}
return result.Success(
"已经成功获取到物业附加费的统计记录。",
response.NewPagedResponse(requestPage, total).ToMap(),
fiber.Map{"charges": fees},
)
} }

View File

@@ -7,84 +7,72 @@ import (
"electricity_bill_calc/security" "electricity_bill_calc/security"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"fmt" "fmt"
"log"
"net/http" "net/http"
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeMeter04kVController(router *gin.Engine) { func InitializeMeter04kVController(router *fiber.App) {
router.GET("/park/:pid/meter/template", download04kvMeterArchiveTemplate) router.Get("/park/:pid/meter/template", download04kvMeterArchiveTemplate)
router.GET("/park/:pid/meters", security.EnterpriseAuthorize, ListPaged04kVMeter) router.Get("/park/:pid/meters", security.EnterpriseAuthorize, ListPaged04kVMeter)
router.GET("/park/:pid/meter/:code", security.EnterpriseAuthorize, fetch04kVMeterDetail) router.Get("/park/:pid/meter/:code", security.EnterpriseAuthorize, fetch04kVMeterDetail)
router.POST("/park/:pid/meter", security.EnterpriseAuthorize, createSingle04kVMeter) 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)
router.POST("/park/:pid/meter/batch", security.EnterpriseAuthorize, batchImport04kVMeterArchive) router.Put("/park/:pid/meter/:code", security.EnterpriseAuthorize, modifySingle04kVMeter)
} }
func download04kvMeterArchiveTemplate(c *gin.Context) { func download04kvMeterArchiveTemplate(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
// if !ensureParkBelongs(c, result, requestParkId) {
// return
// }
parkDetail, err := service.ParkService.FetchParkDetail(requestParkId) parkDetail, err := service.ParkService.FetchParkDetail(requestParkId)
if err != nil { if err != nil {
result.NotFound("未找到指定的园区信息。") return result.NotFound("未找到指定的园区信息。")
return
} }
c.Status(http.StatusOK) return c.Download("./assets/meter_04kv_template.xlsx", fmt.Sprintf("%s-户表档案.xlsx", parkDetail.Name))
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) { func ListPaged04kVMeter(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
requestKeyword := c.DefaultQuery("keyword", "") requestKeyword := c.Query("keyword", "")
meters, totalItem, err := service.Meter04kVService.ListMeterDetail(requestParkId, requestKeyword, requestPage) meters, totalItem, err := service.Meter04kVService.ListMeterDetail(requestParkId, requestKeyword, requestPage)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json( return result.Json(
http.StatusOK, http.StatusOK,
"已获取到符合条件的0.4kV表计集合。", "已获取到符合条件的0.4kV表计集合。",
response.NewPagedResponse(requestPage, totalItem).ToMap(), response.NewPagedResponse(requestPage, totalItem).ToMap(),
gin.H{"meters": meters}, fiber.Map{"meters": meters},
) )
} }
func fetch04kVMeterDetail(c *gin.Context) { func fetch04kVMeterDetail(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
requestMeterCode := c.Param("code") requestMeterCode := c.Params("code")
meter, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) meter, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
if meter == nil { if meter == nil {
result.Json(http.StatusNotFound, "指定的表计信息未能找到。", gin.H{"meter": nil}) return result.Json(http.StatusNotFound, "指定的表计信息未能找到。", fiber.Map{"meter": nil})
return
} }
result.Json(http.StatusOK, "指定的表计信息已找到。", gin.H{"meter": meter}) return result.Json(http.StatusOK, "指定的表计信息已找到。", fiber.Map{"meter": meter})
} }
type _MeterModificationFormData struct { type _MeterModificationFormData struct {
@@ -95,7 +83,6 @@ type _MeterModificationFormData struct {
Ratio decimal.Decimal `json:"ratio" form:"ratio"` Ratio decimal.Decimal `json:"ratio" form:"ratio"`
Seq int `json:"seq" form:"seq"` Seq int `json:"seq" form:"seq"`
IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"` IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"`
WillDilute bool `json:"willDilute" form:"willDilute"`
Enabled bool `json:"enabled" form:"enabled"` Enabled bool `json:"enabled" form:"enabled"`
} }
@@ -108,83 +95,76 @@ type _MeterCreationFormData struct {
Ratio decimal.Decimal `json:"ratio" form:"ratio"` Ratio decimal.Decimal `json:"ratio" form:"ratio"`
Seq int `json:"seq" form:"seq"` Seq int `json:"seq" form:"seq"`
IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"` IsPublicMeter bool `json:"isPublicMeter" form:"isPublicMeter"`
WillDilute bool `json:"willDilute" form:"willDilute"`
Enabled bool `json:"enabled" form:"enabled"` Enabled bool `json:"enabled" form:"enabled"`
} }
func createSingle04kVMeter(c *gin.Context) { func createSingle04kVMeter(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
formData := new(_MeterCreationFormData) formData := new(_MeterCreationFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
log.Printf("[controller|debug] form: %+v", formData) return result.UnableToParse("无法解析提交的数据。")
}
newMeter := new(model.Meter04KV) newMeter := new(model.Meter04KV)
copier.Copy(newMeter, formData) copier.Copy(newMeter, formData)
newMeter.ParkId = requestParkId newMeter.ParkId = requestParkId
log.Printf("[controller|debug] meter: %+v", newMeter)
err := service.Meter04kVService.CreateSingleMeter(*newMeter) err := service.Meter04kVService.CreateSingleMeter(*newMeter)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Created("新0.4kV表计已经添加完成。") return result.Created("新0.4kV表计已经添加完成。")
} }
func modifySingle04kVMeter(c *gin.Context) { func modifySingle04kVMeter(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
requestMeterCode := c.Param("code") requestMeterCode := c.Params("code")
meterDetail, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) meterDetail, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
if meterDetail == nil { if meterDetail == nil {
result.NotFound("指定表计的信息为找到,不能修改。") return result.NotFound("指定表计的信息为找到,不能修改。")
return
} }
formData := new(_MeterModificationFormData) formData := new(_MeterModificationFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
copier.Copy(meterDetail, formData) copier.Copy(meterDetail, formData)
err = service.Meter04kVService.UpdateSingleMeter(meterDetail) err = service.Meter04kVService.UpdateSingleMeter(meterDetail)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定0.4kV表计信息已经更新。") return result.Updated("指定0.4kV表计信息已经更新。")
} }
func batchImport04kVMeterArchive(c *gin.Context) { func batchImport04kVMeterArchive(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
uploadedFile, err := c.FormFile("data") uploadedFile, err := c.FormFile("data")
if err != nil { if err != nil {
result.NotAccept("没有接收到上传的档案文件。") return result.NotAccept("没有接收到上传的档案文件。")
return
} }
archiveFile, err := uploadedFile.Open() archiveFile, err := uploadedFile.Open()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile) analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
records, errs := analyzer.Analysis(*new(model.Meter04KV)) records, errs := analyzer.Analysis(*new(model.Meter04KV))
if len(errs) > 0 { if len(errs) > 0 {
result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", gin.H{"errors": errs}) return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs})
return
} }
mergedMeters := lo.Map(records, func(meter model.Meter04KV, index int) model.Meter04KV { mergedMeters := lo.Map(records, func(meter model.Meter04KV, index int) model.Meter04KV {
@@ -194,13 +174,15 @@ func batchImport04kVMeterArchive(c *gin.Context) {
}) })
errs = service.Meter04kVService.DuplicateMeterCodeValidate(mergedMeters) errs = service.Meter04kVService.DuplicateMeterCodeValidate(mergedMeters)
if len(errs) > 0 { if len(errs) > 0 {
result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", gin.H{"errors": errs}) return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs})
return
} }
err = service.Meter04kVService.BatchCreateMeter(mergedMeters) err = service.Meter04kVService.BatchCreateMeter(mergedMeters)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "上传的表计档案已经全部导入。", gin.H{"errors": make([]excel.ExcelAnalysisError, 0)}) return result.Json(
http.StatusOK,
"上传的表计档案已经全部导入。",
fiber.Map{"errors": make([]excel.ExcelAnalysisError, 0)},
)
} }

View File

@@ -8,64 +8,60 @@ import (
"electricity_bill_calc/tools" "electricity_bill_calc/tools"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeParkController(router *gin.Engine) { func InitializeParkController(router *fiber.App) {
router.GET("/parks", security.EnterpriseAuthorize, listAllParksUnderSessionUser) router.Get("/parks", security.EnterpriseAuthorize, listAllParksUnderSessionUser)
router.GET("/parks/:uid", security.MustAuthenticated, listAllParksUnderSpecificUser) router.Get("/parks/:uid", security.MustAuthenticated, listAllParksUnderSpecificUser)
router.POST("/park", security.EnterpriseAuthorize, createNewPark) router.Post("/park", security.EnterpriseAuthorize, createNewPark)
router.PUT("/park/:pid", security.EnterpriseAuthorize, modifyPark) router.Put("/park/:pid", security.EnterpriseAuthorize, modifyPark)
router.GET("/park/:pid", security.EnterpriseAuthorize, fetchParkDetail) router.Get("/park/:pid", security.EnterpriseAuthorize, fetchParkDetail)
router.PUT("/park/:pid/enabled", security.EnterpriseAuthorize, changeParkEnableState) router.Put("/park/:pid/enabled", security.EnterpriseAuthorize, changeParkEnableState)
router.DELETE("/park/:pid", security.EnterpriseAuthorize, deleteSpecificPark) router.Delete("/park/:pid", security.EnterpriseAuthorize, deleteSpecificPark)
} }
func ensureParkBelongs(c *gin.Context, result *response.Result, requestParkId string) bool { func ensureParkBelongs(c *fiber.Ctx, result *response.Result, requestParkId string) (bool, error) {
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return false, result.Unauthorized(err.Error())
return false
} }
sure, err := service.ParkService.EnsurePark(userSession.Uid, requestParkId) sure, err := service.ParkService.EnsurePark(userSession.Uid, requestParkId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return false, result.Error(http.StatusInternalServerError, err.Error())
return false
} }
if !sure { if !sure {
result.Unauthorized("不能访问不属于自己的园区。") return false, result.Unauthorized("不能访问不属于自己的园区。")
return false
} }
return true return true, nil
} }
func listAllParksUnderSessionUser(c *gin.Context) { func listAllParksUnderSessionUser(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid) keyword := c.Query("keyword")
parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid, keyword)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已获取到指定用户下的园区。", gin.H{"parks": parks}) return result.Json(http.StatusOK, "已获取到指定用户下的园区。", fiber.Map{"parks": parks})
} }
func listAllParksUnderSpecificUser(c *gin.Context) { func listAllParksUnderSpecificUser(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestUserId := c.Param("uid") requestUserId := c.Params("uid")
parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId) keyword := c.Query("keyword")
parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId, keyword)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已获取到指定用户下的园区。", gin.H{"parks": parks}) return result.Json(http.StatusOK, "已获取到指定用户下的园区。", fiber.Map{"parks": parks})
} }
type _ParkInfoFormData struct { type _ParkInfoFormData struct {
@@ -76,20 +72,21 @@ type _ParkInfoFormData struct {
Phone *string `json:"phone" from:"phone"` Phone *string `json:"phone" from:"phone"`
Area decimal.NullDecimal `json:"area" from:"area"` Area decimal.NullDecimal `json:"area" from:"area"`
Capacity decimal.NullDecimal `json:"capacity" from:"capacity"` Capacity decimal.NullDecimal `json:"capacity" from:"capacity"`
Tenement decimal.NullDecimal `json:"tenement" from:"tenement"` TenementQuantity decimal.NullDecimal `json:"tenement" from:"tenement"`
Category int `json:"category" form:"category"` Category int8 `json:"category" form:"category"`
Submeter int `json:"submeter" form:"submeter"` SubmeterType int8 `json:"submeter" form:"submeter"`
} }
func createNewPark(c *gin.Context) { func createNewPark(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
formData := new(_ParkInfoFormData) formData := new(_ParkInfoFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
newPark := new(model.Park) newPark := new(model.Park)
copier.Copy(newPark, formData) copier.Copy(newPark, formData)
newPark.Id = uuid.New().String() newPark.Id = uuid.New().String()
@@ -99,95 +96,90 @@ func createNewPark(c *gin.Context) {
newPark.Enabled = true newPark.Enabled = true
err = service.ParkService.SaveNewPark(*newPark) err = service.ParkService.SaveNewPark(*newPark)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Created("新园区完成创建。") return result.Created("新园区完成创建。")
} }
func modifyPark(c *gin.Context) { func modifyPark(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
formData := new(_ParkInfoFormData) formData := new(_ParkInfoFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
park, err := service.ParkService.FetchParkDetail(requestParkId) park, err := service.ParkService.FetchParkDetail(requestParkId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if userSession.Uid != park.UserId { if userSession.Uid != park.UserId {
result.Unauthorized("不能修改不属于自己的园区。") return result.Unauthorized("不能修改不属于自己的园区。")
return
} }
copier.Copy(park, formData) copier.Copy(park, formData)
nameAbbr := tools.PinyinAbbr(formData.Name) nameAbbr := tools.PinyinAbbr(formData.Name)
park.Abbr = &nameAbbr park.Abbr = &nameAbbr
err = service.ParkService.UpdateParkInfo(park) err = service.ParkService.UpdateParkInfo(park)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定园区资料已更新。") return result.Updated("指定园区资料已更新。")
} }
func fetchParkDetail(c *gin.Context) { func fetchParkDetail(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
park, err := service.ParkService.FetchParkDetail(requestParkId) park, err := service.ParkService.FetchParkDetail(requestParkId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已经获取到指定园区的信息。", gin.H{"park": park}) return result.Json(http.StatusOK, "已经获取到指定园区的信息。", fiber.Map{"park": park})
} }
type _ParkStateFormData struct { type _ParkStateFormData struct {
Enabled bool `json:"enabled" form:"enabled"` Enabled bool `json:"enabled" form:"enabled"`
} }
func changeParkEnableState(c *gin.Context) { func changeParkEnableState(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
formData := new(_ParkStateFormData) formData := new(_ParkStateFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
err = service.ParkService.ChangeParkState(userSession.Uid, requestParkId, formData.Enabled) err = service.ParkService.ChangeParkState(userSession.Uid, requestParkId, formData.Enabled)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定园区的可用性状态已成功更新。") return result.Updated("指定园区的可用性状态已成功更新。")
} }
func deleteSpecificPark(c *gin.Context) { func deleteSpecificPark(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
err = service.ParkService.DeletePark(userSession.Uid, requestParkId) err = service.ParkService.DeletePark(userSession.Uid, requestParkId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
} }
result.Deleted("指定园区已成功删除。") return result.Deleted("指定园区已成功删除。")
} }

View File

@@ -5,40 +5,36 @@ import (
"electricity_bill_calc/service" "electricity_bill_calc/service"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
func InitializeRegionController(router *gin.Engine) { func InitializeRegionController(router *fiber.App) {
router.GET("/region/:rid", fetchRegions) router.Get("/region/:rid", fetchRegions)
router.GET("/regions/:rid", fetchAllLeveledRegions) router.Get("/regions/:rid", fetchAllLeveledRegions)
} }
func fetchRegions(c *gin.Context) { func fetchRegions(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParentId := c.Param("rid") requestParentId := c.Params("rid")
regions, err := service.RegionService.FetchSubRegions(requestParentId) regions, err := service.RegionService.FetchSubRegions(requestParentId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if len(regions) == 0 { if len(regions) == 0 {
result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", gin.H{"regions": make([]string, 0)}) return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)})
return
} }
result.Json(http.StatusOK, "已经获取到相关的行政区划。", gin.H{"regions": regions}) return result.Json(http.StatusOK, "已经获取到相关的行政区划。", fiber.Map{"regions": regions})
} }
func fetchAllLeveledRegions(c *gin.Context) { func fetchAllLeveledRegions(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestRegionCode := c.Param("rid") requestRegionCode := c.Params("rid")
regions, err := service.RegionService.FetchAllParentRegions(requestRegionCode) regions, err := service.RegionService.FetchAllParentRegions(requestRegionCode)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if len(regions) == 0 { if len(regions) == 0 {
result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", gin.H{"regions": make([]string, 0)}) return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)})
return
} }
result.Json(http.StatusOK, "以及获取到相关的行政区划。", gin.H{"regions": regions}) return result.Json(http.StatusOK, "以及获取到相关的行政区划。", fiber.Map{"regions": regions})
} }

View File

@@ -7,136 +7,115 @@ import (
"electricity_bill_calc/security" "electricity_bill_calc/security"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"electricity_bill_calc/tools" "electricity_bill_calc/tools"
"log"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"github.com/fufuok/utils" "github.com/gofiber/fiber/v2"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeReportController(router *gin.Engine) { func InitializeReportController(router *fiber.App) {
router.GET("/reports/with/drafts", security.EnterpriseAuthorize, fetchNewestReportOfParkWithDraft) router.Get("/reports/with/drafts", security.EnterpriseAuthorize, fetchNewestReportOfParkWithDraft)
router.POST("/park/:pid/report", security.EnterpriseAuthorize, initializeNewReport) router.Post("/park/:pid/report", security.EnterpriseAuthorize, initializeNewReport)
router.GET("/report/:rid/step/state", security.EnterpriseAuthorize, fetchReportStepStates) router.Get("/report/:rid/step/state", security.EnterpriseAuthorize, fetchReportStepStates)
router.GET("/report/:rid/summary", security.EnterpriseAuthorize, fetchReportParkSummary) router.Get("/report/:rid/summary", security.EnterpriseAuthorize, fetchReportParkSummary)
router.PUT("/report/:rid/summary", security.EnterpriseAuthorize, fillReportSummary) router.Put("/report/:rid/summary", security.EnterpriseAuthorize, fillReportSummary)
router.GET("/report/:rid/summary/calculate", security.EnterpriseAuthorize, testCalculateReportSummary) router.Get("/report/:rid/summary/calculate", security.EnterpriseAuthorize, testCalculateReportSummary)
router.POST("/report/:rid/summary/calculate", security.EnterpriseAuthorize, progressReportSummary) router.Post("/report/:rid/summary/calculate", security.EnterpriseAuthorize, progressReportSummary)
router.GET("/report/:rid/maintenance", security.EnterpriseAuthorize, fetchWillDilutedFees) router.Put("/report/:rid/step/meter/register", security.EnterpriseAuthorize, progressEndUserRegister)
router.POST("/report/:rid/maintenance", security.EnterpriseAuthorize, createTemporaryWillDilutedFee) router.Post("/report/:rid/publish", security.EnterpriseAuthorize, publishReport)
router.POST("/report/:rid/maintenance/import", security.EnterpriseAuthorize, importPredefinedMaintenanceFees) router.Get("/reports", security.MustAuthenticated, searchReports)
router.PUT("/report/:rid/maintenance/:mid", security.EnterpriseAuthorize, modifyWillDilutedFee) router.Get("/report/:rid", security.MustAuthenticated, fetchReportPublicity)
router.DELETE("/report/:rid/maintenance/:mid", security.EnterpriseAuthorize, deleteTemporaryWillDilutedFee) router.Post("/report/:rid/calculate", security.EnterpriseAuthorize, calculateReport)
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)
} }
func ensureReportBelongs(c *gin.Context, result *response.Result, requestReportId string) bool { func ensureReportBelongs(c *fiber.Ctx, result *response.Result, requestReportId string) (bool, error) {
_, err := _retreiveSession(c) _, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return false, result.Unauthorized(err.Error())
return false
} }
requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId) requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return false, result.NotFound(err.Error())
return false
} }
if requestReport == nil { if requestReport == nil {
result.NotFound("指定报表未能找到。") return false, result.NotFound("指定报表未能找到。")
return false
} }
return ensureParkBelongs(c, result, requestReport.ParkId) return ensureParkBelongs(c, result, requestReport.ParkId)
} }
func fetchNewestReportOfParkWithDraft(c *gin.Context) { func fetchNewestReportOfParkWithDraft(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
parks, err := service.ReportService.FetchParksWithNewestReport(userSession.Uid) parks, err := service.ReportService.FetchParksWithNewestReport(userSession.Uid)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已获取到指定用户下所有园区的最新报表记录。", gin.H{"parks": parks}) return result.Json(http.StatusOK, "已获取到指定用户下所有园区的最新报表记录。", fiber.Map{"parks": parks})
} }
func initializeNewReport(c *gin.Context) { func initializeNewReport(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestParkId := c.Param("pid") requestParkId := c.Params("pid")
userSession, err := _retreiveSession(c) userSession, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
if !ensureParkBelongs(c, result, requestParkId) { if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure {
return return err
} }
requestPeriod := c.Query("period") requestPeriod := c.Query("period")
reportPeriod, err := time.Parse("2006-01", requestPeriod) reportPeriod, err := time.Parse("2006-01", requestPeriod)
if err != nil { if err != nil {
result.NotAccept("提供的初始化期数格式不正确。") return result.NotAccept("提供的初始化期数格式不正确。")
return
} }
valid, err := service.ReportService.IsNewPeriodValid(userSession.Uid, reportPeriod) valid, err := service.ReportService.IsNewPeriodValid(userSession.Uid, requestParkId, reportPeriod)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if !valid { if !valid {
result.NotAccept("只能初始化已发布报表下一个月份的新报表。") return result.NotAccept("只能初始化已发布报表下一个月份的新报表。")
return
} }
newId, err := service.ReportService.InitializeNewReport(requestParkId, reportPeriod) newId, err := service.ReportService.InitializeNewReport(requestParkId, reportPeriod)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Created("新一期报表初始化成功。", gin.H{"reportId": newId}) return result.Created("新一期报表初始化成功。", fiber.Map{"reportId": newId})
} }
func fetchReportStepStates(c *gin.Context) { func fetchReportStepStates(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId) requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json(http.StatusOK, "已经获取到指定报表的填写状态。", gin.H{"steps": requestReport.StepState}) return result.Json(http.StatusOK, "已经获取到指定报表的填写状态。", fiber.Map{"steps": requestReport.StepState})
} }
func fetchReportParkSummary(c *gin.Context) { func fetchReportParkSummary(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
summary, err := service.ReportService.RetreiveReportSummary(requestReportId) summary, err := service.ReportService.RetreiveReportSummary(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
if summary == nil { if summary == nil {
result.NotFound("指定报表未能找到。") return result.NotFound("指定报表未能找到。")
return
} }
result.Json(http.StatusOK, "已经获取到指定报表中的园区概况。", gin.H{"summary": summary}) return result.Json(http.StatusOK, "已经获取到指定报表中的园区概况。", fiber.Map{"summary": summary})
} }
type ReportSummaryFormData struct { type ReportSummaryFormData struct {
@@ -152,327 +131,171 @@ type ReportSummaryFormData struct {
AdjustFee decimal.Decimal `json:"adjustFee" from:"adjustFee"` AdjustFee decimal.Decimal `json:"adjustFee" from:"adjustFee"`
} }
func fillReportSummary(c *gin.Context) { func fillReportSummary(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
formData := new(ReportSummaryFormData) formData := new(ReportSummaryFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
originSummary, err := service.ReportService.RetreiveReportSummary(requestReportId) originSummary, err := service.ReportService.RetreiveReportSummary(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
copier.Copy(originSummary, formData) copier.Copy(originSummary, formData)
originSummary.ReportId = requestReportId
err = service.ReportService.UpdateReportSummary(originSummary) err = service.ReportService.UpdateReportSummary(originSummary)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Updated("指定电费公示报表中的园区概况基本数据已经完成更新。") return result.Updated("指定电费公示报表中的园区概况基本数据已经完成更新。")
} }
func testCalculateReportSummary(c *gin.Context) { func testCalculateReportSummary(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
summary, err := service.ReportService.RetreiveReportSummary(requestReportId) summary, err := service.ReportService.RetreiveReportSummary(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
summary.CalculatePrices() summary.CalculatePrices()
calcResults := tools.ConvertStructToMap(summary) calcResults := tools.ConvertStructToMap(summary)
result.Json(http.StatusOK, "已完成园区概况的试计算。", gin.H{"result": lo.PickByKeys(calcResults, []string{"overallPrice", "criticalPrice", "peakPrice", "flat", "flatFee", "flatPrice", "valleyPrice"})}) return result.Json(
http.StatusOK,
"已完成园区概况的试计算。",
fiber.Map{
"result": lo.PickByKeys(
calcResults,
[]string{"overallPrice", "criticalPrice", "peakPrice", "flat", "flatFee", "flatPrice", "valleyPrice", "consumptionFee"},
),
},
)
} }
func progressReportSummary(c *gin.Context) { func progressReportSummary(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
err := service.ReportService.CalculateSummaryAndFinishStep(requestReportId) err := service.ReportService.CalculateSummaryAndFinishStep(requestReportId)
if err != nil { if err != nil {
if nfErr, ok := err.(exceptions.NotFoundError); ok { if nfErr, ok := err.(exceptions.NotFoundError); ok {
result.NotFound(nfErr.Error()) return result.NotFound(nfErr.Error())
} else { } else {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
} }
return
} }
result.Success("已经完成园区概况的计算,并可以进行到下一步骤。") return result.Success("已经完成园区概况的计算,并可以进行到下一步骤。")
} }
func fetchWillDilutedFees(c *gin.Context) { func progressEndUserRegister(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
}
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) report, err := service.ReportService.RetreiveReportIndex(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return 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) err = service.ReportService.ProgressReportRegisterEndUser(*report)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Success("终端用户抄表编辑步骤已经完成。") return result.Success("终端用户抄表编辑步骤已经完成。")
} }
func publishReport(c *gin.Context) { func publishReport(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
report, err := service.ReportService.RetreiveReportIndex(requestReportId) report, err := service.ReportService.RetreiveReportIndex(requestReportId)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
err = service.ReportService.PublishReport(*report) err = service.ReportService.PublishReport(*report)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Success("指定的公示报表已经发布。") return result.Success("指定的公示报表已经发布。")
} }
func searchReports(c *gin.Context) { func searchReports(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
session, err := _retreiveSession(c) session, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
requestUser := lo. requestUser := lo.
If(session.Type == model.USER_TYPE_ENT, session.Uid). If(session.Type == model.USER_TYPE_ENT, session.Uid).
Else(c.DefaultQuery("user", "")) Else(c.Query("user"))
requestPark := c.DefaultQuery("park", "") requestPark := c.Query("park")
if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT { if len(requestPark) > 0 && session.Type == model.USER_TYPE_ENT {
if !ensureParkBelongs(c, result, requestPark) { if ensure, err := ensureParkBelongs(c, &result, requestPark); !ensure {
return return err
} }
} }
requestPeriodString := c.DefaultQuery("period", "") requestPeriodString := c.Query("period")
var requestPeriod *time.Time = nil var requestPeriod *time.Time = nil
if len(requestPeriodString) > 0 { if len(requestPeriodString) > 0 {
parsedPeriod, err := time.Parse("2006-01", requestPeriodString) parsedPeriod, err := time.Parse("2006-01", requestPeriodString)
if err != nil { if err != nil {
result.NotAccept("参数[period]的格式不正确。") return result.NotAccept("参数[period]的格式不正确。")
return
} }
requestPeriod = lo.ToPtr(parsedPeriod) requestPeriod = lo.ToPtr(parsedPeriod)
} }
requestKeyword := c.DefaultQuery("keyword", "") requestKeyword := c.Query("keyword")
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
records, totalItems, err := service.ReportService.SearchReport(requestUser, requestPark, requestKeyword, requestPeriod, requestPage) requestAllReports, err := strconv.ParseBool(c.Query("all", "false"))
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotAccept("查询参数[all]格式不正确。")
return
} }
result.Success( records, totalItems, err := service.ReportService.SearchReport(requestUser, requestPark, requestKeyword, requestPeriod, requestPage, !requestAllReports)
if err != nil {
return result.NotFound(err.Error())
}
return result.Success(
"已经取得符合条件的公示报表记录。", "已经取得符合条件的公示报表记录。",
response.NewPagedResponse(requestPage, totalItems).ToMap(), response.NewPagedResponse(requestPage, totalItems).ToMap(),
gin.H{"reports": records}, fiber.Map{"reports": records},
) )
} }
func fetchReportPublicity(c *gin.Context) { func fetchReportPublicity(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
publicity, err := service.ReportService.AssembleReportPublicity(requestReportId) publicity, err := service.ReportService.AssembleReportPublicity(requestReportId)
if err != nil { if err != nil {
if nfErr, ok := err.(exceptions.NotFoundError); ok { if nfErr, ok := err.(exceptions.NotFoundError); ok {
result.NotFound(nfErr.Error()) return result.NotFound(nfErr.Error())
return
} else { } else {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} }
result.Success("已经取得指定公示报表的发布版本。", tools.ConvertStructToMap(publicity)) return result.Success("已经取得指定公示报表的发布版本。", tools.ConvertStructToMap(publicity))
} }
func calculateReport(c *gin.Context) { func calculateReport(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
err := service.CalculateService.ComprehensivelyCalculateReport(requestReportId) err := service.CalculateService.ComprehensivelyCalculateReport(requestReportId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Success("指定公示报表中的数据已经计算完毕。") return result.Success("指定公示报表中的数据已经计算完毕。")
} }

View File

@@ -7,34 +7,32 @@ import (
"electricity_bill_calc/service" "electricity_bill_calc/service"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
func InitializeStatisticsController(router *gin.Engine) { func InitializeStatisticsController(router *fiber.App) {
router.GET("/audits", security.OPSAuthorize, currentAuditAmount) router.Get("/audits", security.OPSAuthorize, currentAuditAmount)
router.GET("/stat/reports", security.MustAuthenticated, statReports) router.Get("/stat/reports", security.MustAuthenticated, statReports)
} }
func currentAuditAmount(c *gin.Context) { func currentAuditAmount(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
amount, err := service.WithdrawService.AuditWaits() amount, err := service.WithdrawService.AuditWaits()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已经获取到指定的统计信息。", gin.H{ return result.Json(http.StatusOK, "已经获取到指定的统计信息。", fiber.Map{
"amounts": map[string]int64{ "amounts": map[string]int64{
"withdraw": amount, "withdraw": amount,
}, },
}) })
} }
func statReports(c *gin.Context) { func statReports(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
session, err := _retreiveSession(c) session, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
var ( var (
enterprises int64 = 0 enterprises int64 = 0
@@ -44,34 +42,29 @@ func statReports(c *gin.Context) {
if session.Type != 0 { if session.Type != 0 {
enterprises, err = service.StatisticsService.EnabledEnterprises() enterprises, err = service.StatisticsService.EnabledEnterprises()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
parks, err = service.StatisticsService.EnabledParks() parks, err = service.StatisticsService.EnabledParks()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
reports, err = service.StatisticsService.ParksNewestState() reports, err = service.StatisticsService.ParksNewestState()
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} else { } else {
parks, err = service.StatisticsService.EnabledParks(session.Uid) parks, err = service.StatisticsService.EnabledParks(session.Uid)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
reports, err = service.StatisticsService.ParksNewestState(session.Uid) reports, err = service.StatisticsService.ParksNewestState(session.Uid)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} }
result.Json(http.StatusOK, "已经完成园区报告的统计。", gin.H{ return result.Json(http.StatusOK, "已经完成园区报告的统计。", fiber.Map{
"statistics": gin.H{ "statistics": fiber.Map{
"enterprises": enterprises, "enterprises": enterprises,
"parks": parks, "parks": parks,
"reports": reports, "reports": reports,

View File

@@ -8,28 +8,28 @@ import (
"electricity_bill_calc/response" "electricity_bill_calc/response"
"electricity_bill_calc/security" "electricity_bill_calc/security"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"electricity_bill_calc/tools"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
) )
func InitializeUserController(router *gin.Engine) { func InitializeUserController(router *fiber.App) {
router.DELETE("/password/:uid", security.OPSAuthorize, invalidUserPassword) router.Delete("/password/:uid", security.OPSAuthorize, invalidUserPassword)
router.DELETE("/login", security.MustAuthenticated, logout) router.Delete("/login", security.MustAuthenticated, logout)
router.PUT("/password", resetUserPassword) router.Put("/password", resetUserPassword)
router.GET("/accounts", security.OPSAuthorize, listPagedUser) router.Get("/accounts", security.ManagementAuthorize, listPagedUser)
router.POST("/login", login) router.Post("/login", login)
router.PUT("/account/enabled/state", security.OPSAuthorize, switchUserEnabling) router.Put("/account/enabled/state", security.OPSAuthorize, switchUserEnabling)
router.POST("/account", security.OPSAuthorize, createOPSAndManagementAccount) router.Post("/account", security.OPSAuthorize, createOPSAndManagementAccount)
router.GET("/account/:uid", security.MustAuthenticated, getUserDetail) router.Get("/account/:uid", security.MustAuthenticated, getUserDetail)
router.POST("/enterprise", security.OPSAuthorize, createEnterpriseAccount) router.Post("/enterprise", security.OPSAuthorize, createEnterpriseAccount)
router.PUT("/account/:uid", security.OPSAuthorize, modifyAccountDetail) router.Put("/account/:uid", security.OPSAuthorize, modifyAccountDetail)
router.GET("/enterprise/quick/search", security.OPSAuthorize, quickSearchEnterprise) router.Get("/enterprise/quick/search", security.OPSAuthorize, quickSearchEnterprise)
router.GET("/expiration", security.EnterpriseAuthorize, fetchExpiration) router.Get("/expiration", security.EnterpriseAuthorize, fetchExpiration)
} }
type _LoginFormData struct { type _LoginFormData struct {
@@ -38,16 +38,15 @@ type _LoginFormData struct {
Type int8 `json:"type"` Type int8 `json:"type"`
} }
func login(c *gin.Context) { func login(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
loginData := new(_LoginFormData) loginData := new(_LoginFormData)
err := c.BindJSON(loginData) if err := c.BodyParser(loginData); err != nil {
if err != nil { return result.Error(http.StatusInternalServerError, "表单解析失败。")
result.Error(http.StatusInternalServerError, "表单解析失败。")
return
} }
var ( var (
session *model.Session session *model.Session
err error
) )
if loginData.Type == model.USER_TYPE_ENT { if loginData.Type == model.USER_TYPE_ENT {
session, err = service.UserService.ProcessEnterpriseUserLogin(loginData.Username, loginData.Password) session, err = service.UserService.ProcessEnterpriseUserLogin(loginData.Username, loginData.Password)
@@ -57,51 +56,43 @@ func login(c *gin.Context) {
if err != nil { if err != nil {
if authError, ok := err.(*exceptions.AuthenticationError); ok { if authError, ok := err.(*exceptions.AuthenticationError); ok {
if authError.NeedReset { if authError.NeedReset {
result.LoginNeedReset() return result.LoginNeedReset()
return
} }
result.Error(int(authError.Code), authError.Message) return result.Error(int(authError.Code), authError.Message)
return
} else { } else {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} }
result.LoginSuccess(session) return result.LoginSuccess(session)
} }
func logout(c *gin.Context) { func logout(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
session, exists := c.Get("session") session := c.Locals("session")
if !exists { if session == nil {
result.Success("用户会话已结束。") return result.Success("用户会话已结束。")
return
} }
_, err := cache.ClearSession(session.(*model.Session).Token) _, err := cache.ClearSession(session.(*model.Session).Token)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Success("用户已成功登出系统。") return result.Success("用户已成功登出系统。")
} }
func invalidUserPassword(c *gin.Context) { func invalidUserPassword(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
targetUserId := c.Param("uid") targetUserId := c.Params("uid")
verifyCode, err := service.UserService.InvalidUserPassword(targetUserId) verifyCode, err := service.UserService.InvalidUserPassword(targetUserId)
if _, ok := err.(exceptions.NotFoundError); ok { if _, ok := err.(exceptions.NotFoundError); ok {
result.NotFound("未找到指定用户。") return result.NotFound("未找到指定用户。")
return
} }
if _, ok := err.(exceptions.UnsuccessfulOperationError); ok { if _, ok := err.(exceptions.UnsuccessfulOperationError); ok {
result.NotAccept("未能成功更新用户的密码。") return result.NotAccept("未能成功更新用户的密码。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusAccepted, "用户密码已经失效", gin.H{"verify": verifyCode}) return result.Json(http.StatusAccepted, "用户密码已经失效", fiber.Map{"verify": verifyCode})
} }
type _ResetPasswordFormData struct { type _ResetPasswordFormData struct {
@@ -110,47 +101,42 @@ type _ResetPasswordFormData struct {
NewPassword string `json:"newPass"` NewPassword string `json:"newPass"`
} }
func resetUserPassword(c *gin.Context) { func resetUserPassword(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
resetForm := new(_ResetPasswordFormData) resetForm := new(_ResetPasswordFormData)
c.BindJSON(resetForm) if err := c.BodyParser(resetForm); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
verified, err := service.UserService.VerifyUserPassword(resetForm.Username, resetForm.VerifyCode) verified, err := service.UserService.VerifyUserPassword(resetForm.Username, resetForm.VerifyCode)
if _, ok := err.(exceptions.NotFoundError); ok { if _, ok := err.(exceptions.NotFoundError); ok {
result.NotFound("指定的用户不存在。") return result.NotFound("指定的用户不存在。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if !verified { if !verified {
result.Error(http.StatusUnauthorized, "验证码不正确。") return result.Error(http.StatusUnauthorized, "验证码不正确。")
return
} }
completed, err := service.UserService.ResetUserPassword(resetForm.Username, resetForm.NewPassword) completed, err := service.UserService.ResetUserPassword(resetForm.Username, resetForm.NewPassword)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
if completed { if completed {
result.Updated("用户凭据已更新。") return result.Updated("用户凭据已更新。")
return
} }
result.NotAccept("用户凭据未能成功更新。") return result.NotAccept("用户凭据未能成功更新。")
} }
func listPagedUser(c *gin.Context) { func listPagedUser(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
requestKeyword := c.DefaultQuery("keyword", "") requestKeyword := c.Query("keyword")
requestUserType, err := strconv.Atoi(c.DefaultQuery("type", "-1")) requestUserType, err := strconv.Atoi(c.Query("type", "-1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[type]格式不正确。") return result.NotAccept("查询参数[type]格式不正确。")
return
} }
var requestUserStat *bool var requestUserStat *bool
state, err := strconv.ParseBool(c.Query("state")) state, err := strconv.ParseBool(c.Query("state"))
@@ -161,14 +147,13 @@ func listPagedUser(c *gin.Context) {
} }
users, total, err := service.UserService.ListUserDetail(requestKeyword, requestUserType, requestUserStat, requestPage) users, total, err := service.UserService.ListUserDetail(requestKeyword, requestUserType, requestUserStat, requestPage)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json( return result.Json(
http.StatusOK, http.StatusOK,
"已取得符合条件的用户集合。", "已取得符合条件的用户集合。",
response.NewPagedResponse(requestPage, total).ToMap(), response.NewPagedResponse(requestPage, total).ToMap(),
gin.H{"accounts": users}, fiber.Map{"accounts": users},
) )
} }
@@ -177,21 +162,21 @@ type _UserStateChangeFormData struct {
Enabled bool `json:"enabled" form:"enabled"` Enabled bool `json:"enabled" form:"enabled"`
} }
func switchUserEnabling(c *gin.Context) { func switchUserEnabling(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
switchForm := new(_UserStateChangeFormData) switchForm := new(_UserStateChangeFormData)
c.BindJSON(switchForm) if err := c.BodyParser(switchForm); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
err := service.UserService.SwitchUserState(switchForm.UserID, switchForm.Enabled) err := service.UserService.SwitchUserState(switchForm.UserID, switchForm.Enabled)
if err != nil { if err != nil {
if nfErr, ok := err.(*exceptions.NotFoundError); ok { if nfErr, ok := err.(*exceptions.NotFoundError); ok {
result.NotFound(nfErr.Message) return result.NotFound(nfErr.Message)
return
} else { } else {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} }
result.Updated("用户状态已经更新。") return result.Updated("用户状态已经更新。")
} }
type _OPSAccountCreationFormData struct { type _OPSAccountCreationFormData struct {
@@ -202,18 +187,18 @@ type _OPSAccountCreationFormData struct {
Type int `json:"type" form:"type"` Type int `json:"type" form:"type"`
} }
func createOPSAndManagementAccount(c *gin.Context) { func createOPSAndManagementAccount(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
creationForm := new(_OPSAccountCreationFormData) creationForm := new(_OPSAccountCreationFormData)
c.BindJSON(creationForm) if err := c.BodyParser(creationForm); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
exists, err := service.UserService.IsUsernameExists(creationForm.Username) exists, err := service.UserService.IsUsernameExists(creationForm.Username)
if exists { if exists {
result.Conflict("指定的用户名已经被使用了。") return result.Conflict("指定的用户名已经被使用了。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
newUser := new(model.User) newUser := new(model.User)
newUser.Username = creationForm.Username newUser.Username = creationForm.Username
@@ -224,33 +209,30 @@ func createOPSAndManagementAccount(c *gin.Context) {
newUserDetail.Contact = creationForm.Contact newUserDetail.Contact = creationForm.Contact
newUserDetail.Phone = creationForm.Phone newUserDetail.Phone = creationForm.Phone
newUserDetail.UnitServiceFee = decimal.Zero newUserDetail.UnitServiceFee = decimal.Zero
newUserDetail.ServiceExpiration, _ = time.Parse("2006-01-02 15:04:05", "2099-12-31 23:59:59") newUserDetail.ServiceExpiration, _ = model.ParseDate("2099-12-31")
verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail) verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
cache.AbolishRelation("user") cache.AbolishRelation("user")
result.Json(http.StatusCreated, "用户已经成功创建。", gin.H{"verify": verifyCode}) return result.Json(http.StatusCreated, "用户已经成功创建。", fiber.Map{"verify": verifyCode})
} }
func getUserDetail(c *gin.Context) { func getUserDetail(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
targetUserId := c.Param("uid") targetUserId := c.Params("uid")
exists, err := service.UserService.IsUserExists(targetUserId) exists, err := service.UserService.IsUserExists(targetUserId)
if !exists { if !exists {
result.NotFound("指定的用户不存在。") return result.NotFound("指定的用户不存在。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
} }
userDetail, err := service.UserService.FetchUserDetail(targetUserId) userDetail, err := service.UserService.FetchUserDetail(targetUserId)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "用户详细信息已获取到。", gin.H{"user": userDetail}) return result.Json(http.StatusOK, "用户详细信息已获取到。", fiber.Map{"user": userDetail})
} }
type _EnterpriseCreationFormData struct { type _EnterpriseCreationFormData struct {
@@ -263,18 +245,18 @@ type _EnterpriseCreationFormData struct {
UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"` UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"`
} }
func createEnterpriseAccount(c *gin.Context) { func createEnterpriseAccount(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
creationForm := new(_EnterpriseCreationFormData) creationForm := new(_EnterpriseCreationFormData)
c.BindJSON(creationForm) if err := c.BodyParser(creationForm); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
exists, err := service.UserService.IsUsernameExists(creationForm.Username) exists, err := service.UserService.IsUsernameExists(creationForm.Username)
if exists { if exists {
result.Conflict("指定的用户名已经被使用了。") return result.Conflict("指定的用户名已经被使用了。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
newUser := new(model.User) newUser := new(model.User)
newUser.Username = creationForm.Username newUser.Username = creationForm.Username
@@ -286,18 +268,16 @@ func createEnterpriseAccount(c *gin.Context) {
newUserDetail.Phone = creationForm.Phone newUserDetail.Phone = creationForm.Phone
newUserDetail.UnitServiceFee, err = decimal.NewFromString(*creationForm.UnitServiceFee) newUserDetail.UnitServiceFee, err = decimal.NewFromString(*creationForm.UnitServiceFee)
if err != nil { if err != nil {
result.BadRequest("用户月服务费无法解析。") return result.BadRequest("用户月服务费无法解析。")
return
} }
newUserDetail.ServiceExpiration = time.Now() newUserDetail.ServiceExpiration = model.NewEmptyDate()
verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail) verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
cache.AbolishRelation("user") cache.AbolishRelation("user")
result.Json(http.StatusCreated, "用户已经成功创建。", gin.H{"verify": verifyCode}) return result.Json(http.StatusCreated, "用户已经成功创建。", fiber.Map{"verify": verifyCode})
} }
type _AccountModificationFormData struct { type _AccountModificationFormData struct {
@@ -309,63 +289,69 @@ type _AccountModificationFormData struct {
UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"` UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"`
} }
func modifyAccountDetail(c *gin.Context) { func modifyAccountDetail(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
targetUserId := c.Param("uid") targetUserId := c.Params("uid")
modForm := new(_AccountModificationFormData) modForm := new(_AccountModificationFormData)
c.BindJSON(modForm) if err := c.BodyParser(modForm); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
exists, err := service.UserService.IsUserExists(targetUserId) exists, err := service.UserService.IsUserExists(targetUserId)
if !exists { if !exists {
result.NotFound("指定的用户不存在。") return result.NotFound("指定的用户不存在。")
return
} }
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
newUserInfo := new(model.UserDetail) newUserInfo := new(model.UserDetail)
newUserInfo.Id = targetUserId
newUserInfo.Name = &modForm.Name newUserInfo.Name = &modForm.Name
if len(modForm.Name) > 0 {
abbr := tools.PinyinAbbr(modForm.Name)
newUserInfo.Abbr = &abbr
}
newUserInfo.Region = modForm.Region newUserInfo.Region = modForm.Region
newUserInfo.Address = modForm.Address newUserInfo.Address = modForm.Address
newUserInfo.Contact = modForm.Contact newUserInfo.Contact = modForm.Contact
newUserInfo.Phone = modForm.Phone newUserInfo.Phone = modForm.Phone
newUserInfo.UnitServiceFee, err = decimal.NewFromString(*modForm.UnitServiceFee) newUserInfo.UnitServiceFee, err = decimal.NewFromString(*modForm.UnitServiceFee)
if err != nil { if err != nil {
result.BadRequest("用户月服务费无法解析。") return result.BadRequest("用户月服务费无法解析。")
return
} }
_, err = global.DBConn.ID(targetUserId).Update(newUserInfo) _, err = global.DB.NewUpdate().Model(newUserInfo).
WherePK().
Column("name", "abbr", "region", "address", "contact", "phone", "unit_service_fee").
Exec(c.Context())
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
cache.AbolishRelation("user") cache.AbolishRelation(fmt.Sprintf("user:%s", targetUserId))
cache.AbolishRelation(fmt.Sprintf("user_%s", targetUserId)) return result.Updated("指定用户的信息已经更新。")
result.Updated("指定用户的信息已经更新。")
} }
func quickSearchEnterprise(c *gin.Context) { func quickSearchEnterprise(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
keyword := c.Query("keyword") keyword := c.Query("keyword")
searchResult, err := service.UserService.SearchLimitUsers(keyword, 6) searchResult, err := service.UserService.SearchLimitUsers(keyword, 6)
if err != nil { if err != nil {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
result.Json(http.StatusOK, "已查询到存在符合条件的企业", gin.H{"users": searchResult}) return result.Json(http.StatusOK, "已查询到存在符合条件的企业", fiber.Map{"users": searchResult})
} }
func fetchExpiration(c *gin.Context) { func fetchExpiration(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
session, err := _retreiveSession(c) session, err := _retreiveSession(c)
if err != nil { if err != nil {
result.Unauthorized(err.Error()) return result.Unauthorized(err.Error())
return
} }
user, err := service.UserService.FetchUserDetail(session.Uid) user, err := service.UserService.FetchUserDetail(session.Uid)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json(http.StatusOK, "已经取得用户的服务期限信息", gin.H{"expiration": user.ServiceExpiration.Format("2006-01-02")}) return result.Json(
http.StatusOK,
"已经取得用户的服务期限信息",
fiber.Map{"expiration": user.ServiceExpiration.Format("2006-01-02")},
)
} }

View File

@@ -8,59 +8,53 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
func InitializeWithdrawController(router *gin.Engine) { func InitializeWithdrawController(router *fiber.App) {
router.DELETE("/publicity/:pid", security.EnterpriseAuthorize, applyReportWithdraw) router.Delete("/publicity/:pid", security.EnterpriseAuthorize, applyReportWithdraw)
router.GET("/withdraws", security.OPSAuthorize, fetchWithdrawsWaitingAutdit) router.Get("/withdraws", security.OPSAuthorize, fetchWithdrawsWaitingAutdit)
router.PUT("/withdraw/:rid", security.OPSAuthorize, auditWithdraw) router.Put("/withdraw/:rid", security.OPSAuthorize, auditWithdraw)
} }
func applyReportWithdraw(c *gin.Context) { func applyReportWithdraw(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("pid") requestReportId := c.Params("pid")
if !ensureReportBelongs(c, result, requestReportId) { if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure {
return return err
} }
deleted, err := service.WithdrawService.ApplyWithdraw(requestReportId) deleted, err := service.WithdrawService.ApplyWithdraw(requestReportId)
if err != nil { if err != nil {
if nfErr, ok := err.(exceptions.NotFoundError); ok { if nfErr, ok := err.(exceptions.NotFoundError); ok {
result.NotFound(nfErr.Error()) return result.NotFound(nfErr.Error())
return
} else if ioErr, ok := err.(exceptions.ImproperOperateError); ok { } else if ioErr, ok := err.(exceptions.ImproperOperateError); ok {
result.NotAccept(ioErr.Error()) return result.NotAccept(ioErr.Error())
return
} else { } else {
result.Error(http.StatusInternalServerError, err.Error()) return result.Error(http.StatusInternalServerError, err.Error())
return
} }
} }
if !deleted { if !deleted {
result.Error(http.StatusInternalServerError, "未能完成公示报表的申请撤回操作。") return result.Error(http.StatusInternalServerError, "未能完成公示报表的申请撤回操作。")
return
} }
result.Success("指定的公示报表已经申请撤回。") return result.Success("指定的公示报表已经申请撤回。")
} }
func fetchWithdrawsWaitingAutdit(c *gin.Context) { func fetchWithdrawsWaitingAutdit(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
keyword := c.DefaultQuery("keyword", "") keyword := c.Query("keyword")
requestPage, err := strconv.Atoi(c.DefaultQuery("page", "1")) requestPage, err := strconv.Atoi(c.Query("page", "1"))
if err != nil { if err != nil {
result.NotAccept("查询参数[page]格式不正确。") return result.NotAccept("查询参数[page]格式不正确。")
return
} }
reports, totalitems, err := service.WithdrawService.FetchPagedWithdrawApplies(requestPage, keyword) reports, totalitems, err := service.WithdrawService.FetchPagedWithdrawApplies(requestPage, keyword)
if err != nil { if err != nil {
result.NotFound(err.Error()) return result.NotFound(err.Error())
return
} }
result.Json( return result.Json(
http.StatusOK, http.StatusOK,
"已经取得符合条件的等待审核的撤回申请。", "已经取得符合条件的等待审核的撤回申请。",
response.NewPagedResponse(requestPage, totalitems).ToMap(), response.NewPagedResponse(requestPage, totalitems).ToMap(),
gin.H{"records": reports}, fiber.Map{"records": reports},
) )
} }
@@ -68,19 +62,20 @@ type WithdrawAuditFormData struct {
Audit bool `json:"audit" form:"audit"` Audit bool `json:"audit" form:"audit"`
} }
func auditWithdraw(c *gin.Context) { func auditWithdraw(c *fiber.Ctx) error {
result := response.NewResult(c) result := response.NewResult(c)
requestReportId := c.Param("rid") requestReportId := c.Params("rid")
formData := new(WithdrawAuditFormData) formData := new(WithdrawAuditFormData)
c.BindJSON(formData) if err := c.BodyParser(formData); err != nil {
return result.UnableToParse("无法解析提交的数据。")
}
err := service.WithdrawService.AuditWithdraw(requestReportId, formData.Audit) err := service.WithdrawService.AuditWithdraw(requestReportId, formData.Audit)
if err != nil { if err != nil {
if nfErr, ok := err.(exceptions.NotFoundError); ok { if nfErr, ok := err.(exceptions.NotFoundError); ok {
result.NotFound(nfErr.Error()) return result.NotFound(nfErr.Error())
} else { } else {
result.NotAccept(err.Error()) return result.NotAccept(err.Error())
} }
return
} }
result.Success("指定公示报表的撤回申请已经完成审核") return result.Success("指定公示报表的撤回申请已经完成审核")
} }

View File

@@ -4,6 +4,7 @@ import (
"electricity_bill_calc/model" "electricity_bill_calc/model"
"electricity_bill_calc/tools" "electricity_bill_calc/tools"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
@@ -25,6 +26,7 @@ type ColumnRecognizer struct {
Pattern []string Pattern []string
Tag string Tag string
MatchIndex int MatchIndex int
MustFill bool
} }
type ExcelAnalyzer[T any] struct { type ExcelAnalyzer[T any] struct {
@@ -126,8 +128,18 @@ func (a *ExcelAnalyzer[T]) Analysis(bean T) ([]T, []ExcelAnalysisError) {
if alias, ok := field.Tag.Lookup("excel"); ok { if alias, ok := field.Tag.Lookup("excel"); ok {
for _, recognizer := range a.Regconizers { for _, recognizer := range a.Regconizers {
if alias == recognizer.Tag && recognizer.MatchIndex != -1 { if alias == recognizer.Tag && recognizer.MatchIndex != -1 {
var matchValue string
actualField := instance.Elem().FieldByName(field.Name) 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() { switch field.Type.String() {
case "string": case "string":
actualField.Set(reflect.ValueOf(matchValue)) actualField.Set(reflect.ValueOf(matchValue))
@@ -154,7 +166,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)}}) errs = append(errs, ExcelAnalysisError{Row: rowIndex + 1, Col: recognizer.MatchIndex + 1, Err: AnalysisError{Err: fmt.Errorf("单元格内容应为纯数字内容。%w", err)}})
actualField.Set(reflect.ValueOf((nullValue))) actualField.Set(reflect.ValueOf((nullValue)))
} else { } else {
actualField.Set(reflect.ValueOf(decimalValue)) value := decimal.NewNullDecimal(decimalValue)
value.Valid = true
actualField.Set(reflect.ValueOf(value))
} }
} }
case "int64", "int": case "int64", "int":

View File

@@ -7,12 +7,16 @@ import (
var ( var (
endUserNonPVRecognizers = []*ColumnRecognizer{ endUserNonPVRecognizers = []*ColumnRecognizer{
{Pattern: []string{"电表编号"}, Tag: "meterId", MatchIndex: -1}, {Pattern: []string{"电表编号"}, Tag: "meterId", MatchIndex: -1, MustFill: true},
{Pattern: []string{"期", "(总)"}, Tag: "currentPeriodOverall", MatchIndex: -1}, {Pattern: []string{"期", "(总)"}, Tag: "lastPeriodOverall", MatchIndex: -1, MustFill: true},
{Pattern: []string{"退补", "(总)"}, Tag: "adjustOverall", MatchIndex: -1}, {Pattern: []string{"本期", "(总)"}, Tag: "currentPeriodOverall", MatchIndex: -1, MustFill: true},
{Pattern: []string{"退补", "(总)"}, Tag: "adjustOverall", MatchIndex: -1, MustFill: true},
} }
endUserPVRecognizers = append( endUserPVRecognizers = append(
endUserNonPVRecognizers, endUserNonPVRecognizers,
&ColumnRecognizer{Pattern: []string{"上期", "(尖峰)"}, Tag: "lastPeriodCritical", MatchIndex: -1},
&ColumnRecognizer{Pattern: []string{"上期", "(峰)"}, Tag: "lastPeriodPeak", MatchIndex: -1},
&ColumnRecognizer{Pattern: []string{"上期", "(谷)"}, Tag: "lastPeriodValley", MatchIndex: -1},
&ColumnRecognizer{Pattern: []string{"本期", "(尖峰)"}, Tag: "currentPeriodCritical", MatchIndex: -1}, &ColumnRecognizer{Pattern: []string{"本期", "(尖峰)"}, Tag: "currentPeriodCritical", MatchIndex: -1},
&ColumnRecognizer{Pattern: []string{"本期", "(峰)"}, Tag: "currentPeriodPeak", MatchIndex: -1}, &ColumnRecognizer{Pattern: []string{"本期", "(峰)"}, Tag: "currentPeriodPeak", MatchIndex: -1},
&ColumnRecognizer{Pattern: []string{"本期", "(谷)"}, Tag: "currentPeriodValley", MatchIndex: -1}, &ColumnRecognizer{Pattern: []string{"本期", "(谷)"}, Tag: "currentPeriodValley", MatchIndex: -1},

View File

@@ -6,15 +6,14 @@ import (
) )
var meter04kVExcelRecognizers = []*ColumnRecognizer{ var meter04kVExcelRecognizers = []*ColumnRecognizer{
{Pattern: []string{"表号"}, Tag: "code", MatchIndex: -1}, {Pattern: []string{"表号"}, Tag: "code", MatchIndex: -1, MustFill: true},
{Pattern: []string{"户名"}, Tag: "name", MatchIndex: -1}, {Pattern: []string{"户名"}, Tag: "name", MatchIndex: -1},
{Pattern: []string{"户址"}, Tag: "address", MatchIndex: -1}, {Pattern: []string{"户址"}, Tag: "address", MatchIndex: -1},
{Pattern: []string{"联系人"}, Tag: "contact", MatchIndex: -1}, {Pattern: []string{"联系人"}, Tag: "contact", MatchIndex: -1},
{Pattern: []string{"电话"}, Tag: "phone", MatchIndex: -1}, {Pattern: []string{"电话"}, Tag: "phone", MatchIndex: -1},
{Pattern: []string{"倍率"}, Tag: "ratio", MatchIndex: -1}, {Pattern: []string{"倍率"}, Tag: "ratio", MatchIndex: -1, MustFill: true},
{Pattern: []string{"序号"}, Tag: "seq", MatchIndex: -1}, {Pattern: []string{"序号"}, Tag: "seq", MatchIndex: -1, MustFill: true},
{Pattern: []string{"公用设备"}, Tag: "public", MatchIndex: -1}, {Pattern: []string{"公用设备"}, Tag: "public", MatchIndex: -1, MustFill: true},
{Pattern: []string{"摊薄"}, Tag: "dilute", MatchIndex: -1},
} }
func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.Meter04KV], error) { func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.Meter04KV], error) {

View File

@@ -75,8 +75,8 @@ func (t *MeterNonPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUser
meter.MeterId, meter.MeterId,
meter.Ratio, meter.Ratio,
meter.LastPeriodOverall, meter.LastPeriodOverall,
nil, meter.CurrentPeriodOverall,
nil, meter.AdjustOverall,
}, },
excelize.RowOpts{Height: 15}, excelize.RowOpts{Height: 15},
); err != nil { ); err != nil {

View File

@@ -84,17 +84,17 @@ func (t *MeterPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDet
meter.MeterId, meter.MeterId,
meter.Ratio, meter.Ratio,
meter.LastPeriodOverall, meter.LastPeriodOverall,
nil, meter.CurrentPeriodOverall,
meter.LastPeriodCritical, meter.LastPeriodCritical,
nil, meter.CurrentPeriodCritical,
meter.LastPeriodPeak, meter.LastPeriodPeak,
nil, meter.CurrentPeriodPeak,
meter.LastPeriodValley, meter.LastPeriodValley,
nil, meter.CurrentPeriodValley,
nil, meter.AdjustOverall,
nil, meter.AdjustCritical,
nil, meter.AdjustPeak,
nil, meter.AdjustValley,
}, },
excelize.RowOpts{Height: 15}, excelize.RowOpts{Height: 15},
); err != nil { ); err != nil {

16
global/context.go Normal file
View 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)
}

View File

@@ -1,55 +1,60 @@
package global package global
import ( import (
"database/sql"
"fmt" "fmt"
"strings"
"time" "time"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/logger"
// _ "github.com/lib/pq" "github.com/uptrace/bun"
_ "github.com/jackc/pgx/v5/stdlib" "github.com/uptrace/bun/dialect/pgdialect"
"xorm.io/xorm" "github.com/uptrace/bun/driver/pgdriver"
"xorm.io/xorm/log" "go.uber.org/zap/zapcore"
) )
var ( var (
DBConn *xorm.Engine DB *bun.DB
) )
func SetupDatabaseConnection() error { func SetupDatabaseConnection() error {
var err error // connStr := fmt.Sprintf(
// 以下连接方式是采用pgx驱动的时候使用的。 // "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai connect_timeout=0 tcp_user_timeout=180000",
DBConn, err = xorm.NewEngine("pgx", fmt.Sprintf(
"postgresql://%s:%s@%s:%d/%s?sslmode=disable&",
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.Host,
// config.DatabaseSettings.User, // config.DatabaseSettings.User,
// config.DatabaseSettings.Pass, // config.DatabaseSettings.Pass,
// config.DatabaseSettings.DB, // config.DatabaseSettings.DB,
// config.DatabaseSettings.Port, // config.DatabaseSettings.Port,
// )) // )
if err != nil { pgconn := pgdriver.NewConnector(
return err pgdriver.WithNetwork("tcp"),
} pgdriver.WithAddr(fmt.Sprintf("%s:%d", config.DatabaseSettings.Host,
config.DatabaseSettings.Port)),
DBConn.Ping() pgdriver.WithUser(config.DatabaseSettings.User),
DBConn.SetMaxIdleConns(config.DatabaseSettings.MaxIdleConns) pgdriver.WithInsecure(true),
DBConn.SetMaxOpenConns(config.DatabaseSettings.MaxOpenConns) pgdriver.WithPassword(config.DatabaseSettings.Pass),
DBConn.SetConnMaxLifetime(60 * time.Second) pgdriver.WithDatabase(config.DatabaseSettings.DB),
pgdriver.WithDialTimeout(30*time.Second),
if strings.ToLower(config.ServerSettings.RunMode) == "debug" { pgdriver.WithReadTimeout(3*time.Minute),
DBConn.ShowSQL(true) pgdriver.WithWriteTimeout(10*time.Minute),
DBConn.Logger().SetLevel(log.LOG_DEBUG) )
} sqldb := sql.OpenDB(pgconn)
DB = bun.NewDB(sqldb, pgdialect.New())
DB.AddQueryHook(logger.NewQueryHook(logger.QueryHookOptions{
LogSlow: 3 * time.Second,
Logger: logger.Named("PG"),
QueryLevel: zapcore.DebugLevel,
ErrorLevel: zapcore.ErrorLevel,
SlowLevel: zapcore.WarnLevel,
ErrorTemplate: "{{.Operation}}[{{.Duration}}]: {{.Query}}: {{.Error}}",
MessageTemplate: "{{.Operation}}[{{.Duration}}]: {{.Query}}",
}))
DB.SetMaxIdleConns(config.DatabaseSettings.MaxIdleConns)
DB.SetMaxOpenConns(config.DatabaseSettings.MaxOpenConns)
DB.SetConnMaxIdleTime(10 * time.Minute)
DB.SetConnMaxLifetime(60 * time.Minute)
DB.Ping()
return nil return nil
} }

View File

@@ -9,13 +9,13 @@ import (
) )
var ( var (
RedisConn rueidis.Client Rd rueidis.Client
Ctx = context.Background() Ctx = context.Background()
) )
func SetupRedisConnection() error { func SetupRedisConnection() error {
var err error var err error
RedisConn, err = rueidis.NewClient(rueidis.ClientOption{ Rd, err = rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)}, InitAddress: []string{fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)},
Password: config.RedisSettings.Password, Password: config.RedisSettings.Password,
SelectDB: config.RedisSettings.DB, SelectDB: config.RedisSettings.DB,
@@ -23,8 +23,8 @@ func SetupRedisConnection() error {
if err != nil { if err != nil {
return err return err
} }
pingCmd := RedisConn.B().Ping().Build() pingCmd := Rd.B().Ping().Build()
result := RedisConn.Do(Ctx, pingCmd) result := Rd.Do(Ctx, pingCmd)
if result.Error() != nil { if result.Error() != nil {
return result.Error() return result.Error()
} }

45
go.mod
View File

@@ -5,9 +5,8 @@ go 1.19
require ( require (
github.com/deckarep/golang-set/v2 v2.1.0 github.com/deckarep/golang-set/v2 v2.1.0
github.com/fufuok/utils v0.7.13 github.com/fufuok/utils v0.7.13
github.com/gin-gonic/gin v1.8.1 github.com/gofiber/fiber/v2 v2.38.1
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/jackc/pgx/v5 v5.0.0-beta.1
github.com/jinzhu/copier v0.3.5 github.com/jinzhu/copier v0.3.5
github.com/liamylian/jsontime/v2 v2.0.0 github.com/liamylian/jsontime/v2 v2.0.0
github.com/mozillazg/go-pinyin v0.19.0 github.com/mozillazg/go-pinyin v0.19.0
@@ -15,31 +14,35 @@ require (
github.com/samber/lo v1.27.0 github.com/samber/lo v1.27.0
github.com/shopspring/decimal v1.3.1 github.com/shopspring/decimal v1.3.1
github.com/spf13/viper v1.12.0 github.com/spf13/viper v1.12.0
github.com/valyala/fasthttp v1.40.0
github.com/xuri/excelize/v2 v2.6.1 github.com/xuri/excelize/v2 v2.6.1
xorm.io/builder v0.3.12 go.uber.org/zap v1.23.0
xorm.io/xorm v1.3.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/klauspost/compress v1.15.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // 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
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
mellium.im/sasl v0.3.0 // indirect
) )
require ( require (
github.com/fsnotify/fsnotify v1.5.4 // indirect 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/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/json-iterator/go v1.1.12 // 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/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // 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 v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect
@@ -49,16 +52,18 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.3.0 // indirect github.com/subosito/gotenv v1.3.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect github.com/uptrace/bun v1.1.8
github.com/ugorji/go/codec v1.2.7 // indirect 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-20220603152613-6918739fd470 // indirect github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect
golang.org/x/text v0.3.7 // 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 gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

598
go.sum
View File

@@ -36,126 +36,42 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fufuok/utils v0.7.13 h1:FGx8Mnfg0ZB8HdVz1X60JJ2kFu1rtcsFDYUxUTzNKkU= github.com/fufuok/utils v0.7.13 h1:FGx8Mnfg0ZB8HdVz1X60JJ2kFu1rtcsFDYUxUTzNKkU=
github.com/fufuok/utils v0.7.13/go.mod h1:ztIaorPqZGdbvmW3YlwQp80K8rKJmEy6xa1KwpJSsmk= github.com/fufuok/utils v0.7.13/go.mod h1:ztIaorPqZGdbvmW3YlwQp80K8rKJmEy6xa1KwpJSsmk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -180,11 +96,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -195,9 +106,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -214,170 +123,41 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/pgx/v5 v5.0.0-beta.1 h1:tQXW/iBC2BYQ/SZK/PBpiW1Cn5/6vMzLBv8Unphg2Xw=
github.com/jackc/pgx/v5 v5.0.0-beta.1/go.mod h1:QJ8xU09HYKHOccHeisi/6sXeRG4dd3AxuV7cmKET4WA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0= github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0=
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY= github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -391,129 +171,42 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c= github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rueian/rueidis v0.0.73 h1:+r0Z6C6HMnkquPgY3zaHVpTqmCyJL56Z36GSlyBrufk= github.com/rueian/rueidis v0.0.73 h1:+r0Z6C6HMnkquPgY3zaHVpTqmCyJL56Z36GSlyBrufk=
github.com/rueian/rueidis v0.0.73/go.mod h1:FwnfDILF2GETrvXcYFlhIiru/7NmSIm1f+7C5kutO0I= github.com/rueian/rueidis v0.0.73/go.mod h1:FwnfDILF2GETrvXcYFlhIiru/7NmSIm1f+7C5kutO0I=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ= github.com/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ=
github.com/samber/lo v1.27.0/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= github.com/samber/lo v1.27.0/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -525,16 +218,25 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o=
github.com/uptrace/bun/driver/pgdriver v1.1.8/go.mod h1:4tHK0h7a/UoldBoe9J3GU4tEYjr3mkd62U3Kq3PVk3E=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c= github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k= github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k=
@@ -545,51 +247,31 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -627,27 +309,18 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -658,7 +331,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -669,6 +341,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -690,37 +363,19 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -736,24 +391,19 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -765,32 +415,24 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -798,7 +440,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -819,22 +460,15 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -855,7 +489,6 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -867,7 +500,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -899,15 +531,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -930,30 +557,16 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -961,7 +574,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -969,122 +581,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= mellium.im/sasl v0.3.0 h1:0qoaTCTo5Py7u/g0cBIQZcMOgG/5LM71nshbXwznBh8=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= mellium.im/sasl v0.3.0/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8=
modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE=
modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM=
xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=

163
logger/bunhook.go Normal file
View File

@@ -0,0 +1,163 @@
package logger
import (
"bytes"
"context"
"database/sql"
"fmt"
"strings"
"text/template"
"time"
"github.com/uptrace/bun"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// QueryHookOptions logging options
type QueryHookOptions struct {
LogSlow time.Duration
Logger *zap.Logger
QueryLevel zapcore.Level
SlowLevel zapcore.Level
ErrorLevel zapcore.Level
MessageTemplate string
ErrorTemplate string
}
// QueryHook wraps query hook
type QueryHook struct {
opts QueryHookOptions
errorTemplate *template.Template
messageTemplate *template.Template
}
// LogEntryVars variables made available t otemplate
type LogEntryVars struct {
Timestamp time.Time
Query string
Operation string
Duration time.Duration
Error error
}
// NewQueryHook returns new instance
func NewQueryHook(opts QueryHookOptions) *QueryHook {
h := new(QueryHook)
if opts.ErrorTemplate == "" {
opts.ErrorTemplate = "{{.Operation}}[{{.Duration}}]: {{.Query}}: {{.Error}}"
}
if opts.MessageTemplate == "" {
opts.MessageTemplate = "{{.Operation}}[{{.Duration}}]: {{.Query}}"
}
h.opts = opts
errorTemplate, err := template.New("ErrorTemplate").Parse(h.opts.ErrorTemplate)
if err != nil {
panic(err)
}
messageTemplate, err := template.New("MessageTemplate").Parse(h.opts.MessageTemplate)
if err != nil {
panic(err)
}
h.errorTemplate = errorTemplate
h.messageTemplate = messageTemplate
return h
}
// BeforeQuery does nothing tbh
func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context {
return ctx
}
// AfterQuery convert a bun QueryEvent into a logrus message
func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
var level zapcore.Level
var isError bool
var msg bytes.Buffer
now := time.Now()
dur := now.Sub(event.StartTime)
switch event.Err {
case nil, sql.ErrNoRows:
isError = false
if h.opts.LogSlow > 0 && dur >= h.opts.LogSlow {
level = h.opts.SlowLevel
} else {
level = h.opts.QueryLevel
}
default:
isError = true
level = h.opts.ErrorLevel
}
if level == 0 {
return
}
args := &LogEntryVars{
Timestamp: now,
Query: string(event.Query),
Operation: eventOperation(event),
Duration: dur,
Error: event.Err,
}
if isError {
if err := h.errorTemplate.Execute(&msg, args); err != nil {
panic(err)
}
} else {
if err := h.messageTemplate.Execute(&msg, args); err != nil {
panic(err)
}
}
switch level {
case zapcore.DebugLevel:
h.opts.Logger.Debug(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs))
case zapcore.InfoLevel:
h.opts.Logger.Info(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs))
case zapcore.WarnLevel:
h.opts.Logger.Warn(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs))
case zapcore.ErrorLevel:
h.opts.Logger.Error(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err))
case zapcore.FatalLevel:
h.opts.Logger.Fatal(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err))
case zapcore.PanicLevel:
h.opts.Logger.Panic(msg.String(), zap.String("query", event.Query), zap.Any("args", event.QueryArgs), zap.Error(event.Err))
default:
panic(fmt.Errorf("unsupported level: %v", level))
}
}
// taken from bun
func eventOperation(event *bun.QueryEvent) string {
switch event.QueryAppender.(type) {
case *bun.SelectQuery:
return "SELECT"
case *bun.InsertQuery:
return "INSERT"
case *bun.UpdateQuery:
return "UPDATE"
case *bun.DeleteQuery:
return "DELETE"
case *bun.CreateTableQuery:
return "CREATE TABLE"
case *bun.DropTableQuery:
return "DROP TABLE"
}
return queryOperation(event.Query)
}
// taken from bun
func queryOperation(name string) string {
if idx := strings.Index(name, " "); idx > 0 {
name = name[:idx]
}
if len(name) > 16 {
name = name[:16]
}
return string(name)
}

132
logger/logger.go Normal file
View File

@@ -0,0 +1,132 @@
package logger
import (
"os"
"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("Logger initialized.")
}
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()
}

87
logger/middleware.go Normal file
View File

@@ -0,0 +1,87 @@
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("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
View 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
}

23
logger/rolling.go Normal file
View File

@@ -0,0 +1,23 @@
package logger
import (
"io"
"log"
"os"
"gopkg.in/natefinch/lumberjack.v2"
)
func newRollingWriter() io.Writer {
if err := os.MkdirAll("log", 0744); err != nil {
log.Println("不能创建用于保存日志的目录。")
return nil
}
return &lumberjack.Logger{
Filename: "log/service.log",
MaxBackups: 366 * 10, // files
MaxSize: 200, // megabytes
MaxAge: 366 * 10, // days
}
}

114
main.go
View File

@@ -1,81 +1,80 @@
package main package main
import ( import (
"database/sql"
"electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/migration"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"electricity_bill_calc/router" "electricity_bill_calc/router"
"electricity_bill_calc/service" "electricity_bill_calc/service"
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"io" "io"
"log"
"os" "os"
"strconv" "strconv"
"time" "time"
"github.com/gin-gonic/gin"
jsontime "github.com/liamylian/jsontime/v2/v2"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun/migrate"
"go.uber.org/zap"
) )
func init() { func init() {
l := logger.Named("Init")
err := config.SetupSetting() err := config.SetupSetting()
if err != nil { if err != nil {
log.Fatalf("Configuration load failed: %v", err) l.Fatal("Configuration load failed.", zap.Error(err))
} }
log.Println("Configuration loaded!") l.Info("Configuration loaded!")
err = global.SetupDatabaseConnection() err = global.SetupDatabaseConnection()
if err != nil { if err != nil {
log.Fatalf("Main Database connect failed: %v", err) l.Fatal("Main Database connect failed.", zap.Error(err))
} }
log.Println("Main Database connected!") l.Info("Main Database connected!")
err = global.DBConn.Sync( migrator := migrate.NewMigrator(global.DB, migration.Migrations)
&model.Region{}, ctx, cancel := global.TimeoutContext(12)
&model.User{}, defer cancel()
&model.UserDetail{}, err = migrator.Init(ctx)
&model.UserCharge{},
&model.Park{},
&model.Meter04KV{},
&model.MaintenanceFee{},
&model.Report{},
&model.ReportSummary{},
&model.WillDilutedFee{},
&model.EndUserDetail{})
if err != nil { if err != nil {
log.Fatalf("Database structure synchronize failed: %v", err) l.Fatal("Database migration unable to initialized.", zap.Error(err))
}
group, err := migrator.Migrate(ctx)
if err != nil {
l.Fatal("Database migrate failed.", zap.Error(err))
}
if group.IsZero() {
l.Info("There are no new migrations to run (database is up to date)")
} }
log.Println("Database structure synchronized.")
err = global.SetupRedisConnection() err = global.SetupRedisConnection()
if err != nil { if err != nil {
log.Fatalf("Main Cache Database connect failed: %v", err) l.Fatal("Main Cache Database connect failed.", zap.Error(err))
} }
log.Println("Main Cache Database connected!") l.Info("Main Cache Database connected!")
err = initializeRegions() err = initializeRegions()
if err != nil { if err != nil {
log.Fatalf("Regions initialize failed: %v", err) l.Fatal("Regions initialize failed.", zap.Error(err))
} }
log.Println("Regions synchronized.") l.Info("Regions synchronized.")
err = intializeSingularity() err = intializeSingularity()
if err != nil { if err != nil {
log.Fatalf("Singularity account intialize failed: %v", err) l.Fatal("Singularity account intialize failed.", zap.Error(err))
} }
log.Println("Singularity account intialized.") l.Info("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 { func initializeRegions() error {
log.Println("Synchronize regions...") ctx, cancel := global.TimeoutContext()
defer cancel()
logger.Info("Synchronize regions...")
regionCsvFile, err := os.Open("regions.csv") regionCsvFile, err := os.Open("regions.csv")
if err != nil { if err != nil {
return fmt.Errorf("region initialize file is not found: %w", err) return fmt.Errorf("region initialize file is not found: %w", err)
@@ -83,15 +82,16 @@ func initializeRegions() error {
defer regionCsvFile.Close() defer regionCsvFile.Close()
var existRegions = make([]string, 0) var existRegions = make([]string, 0)
err = global.DBConn.Table("region").Cols("code").Find(&existRegions) err = global.DB.NewSelect().Model((*model.Region)(nil)).
Column("code").
Scan(ctx, &existRegions)
if err != nil { if err != nil {
return fmt.Errorf("unable to retreive regions from database: %w", err) return fmt.Errorf("unable to retreive regions from database: %w", err)
} }
regionCsv := csv.NewReader(regionCsvFile) regionCsv := csv.NewReader(regionCsvFile)
transaction := global.DBConn.NewSession() transaction, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
defer transaction.Close() if err != nil {
if err = transaction.Begin(); err != nil {
return fmt.Errorf("unable to intiate database transaction: %w", err) return fmt.Errorf("unable to intiate database transaction: %w", err)
} }
for { for {
@@ -106,7 +106,12 @@ func initializeRegions() error {
if err != nil { if err != nil {
continue continue
} }
if _, err = transaction.Insert(&model.Region{Code: record[0], Name: record[1], Level: level, Parent: record[3]}); err != nil { if _, err := transaction.NewInsert().Model(&model.Region{
Code: record[0],
Name: record[1],
Level: level,
Parent: record[3],
}).Exec(ctx); err != nil {
return fmt.Errorf("region synchronize in failed: %v, %w", record, err) return fmt.Errorf("region synchronize in failed: %v, %w", record, err)
} }
} }
@@ -131,7 +136,7 @@ func intializeSingularity() error {
Enabled: true, Enabled: true,
} }
singularityName := "Singularity" singularityName := "Singularity"
singularityExpires, err := time.Parse("2006-01-02 15:04:05", "2099-12-31 23:59:59") singularityExpires, err := model.ParseDate("2099-12-31")
if err != nil { if err != nil {
return fmt.Errorf("singularity expires time parse failed: %w", err) return fmt.Errorf("singularity expires time parse failed: %w", err)
} }
@@ -144,22 +149,39 @@ func intializeSingularity() error {
if err != nil { if err != nil {
return fmt.Errorf("singularity account failed to create: %w", err) 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("Singularity account created, use %s as verify code to reset password.", verifyCode),
zap.String("account", "singularity"),
zap.String("verifyCode", verifyCode),
)
return nil return nil
} }
func DBConnectionKeepLive() { func DBConnectionKeepLive() {
for range time.Tick(30 * time.Second) { for range time.Tick(30 * time.Second) {
err := global.DBConn.Ping() err := global.DB.Ping()
if err != nil { if err != nil {
continue continue
} }
} }
} }
func main() { func RedisOrphanCleanup() {
go DBConnectionKeepLive() cleanLogger := logger.Named("Cache").With(zap.String("function", "Cleanup"))
gin.SetMode(config.ServerSettings.RunMode) for range time.Tick(2 * time.Minute) {
r := router.Router() cleanLogger.Info("Proceeding cleanup orphan keys.")
r.Run(fmt.Sprintf(":%d", config.ServerSettings.HttpPort)) err := cache.ClearOrphanRelationItems()
if err != nil {
cleanLogger.Error("Orphan keys clear failed.")
continue
}
}
}
func main() {
// 本次停用检测的原因是使用Ping来保持数据库链接看起来没有什么用处。
// go DBConnectionKeepLive()
go RedisOrphanCleanup()
app := router.App()
app.Listen(fmt.Sprintf(":%d", config.ServerSettings.HttpPort))
} }

View File

@@ -0,0 +1,240 @@
create table if not exists region (
code varchar(15) not null primary key,
name varchar(50) not null,
level smallint not null default 0,
parent varchar(15) not null default '0'
);
create table if not exists `user` (
created_at timestamptz not null,
id varchar(120) not null primary key,
username varchar(30) not null,
password varchar(256) not null,
reset_needed boolean not null default false,
type smallint not null,
enabled boolean not null default true
);
create table if not exists user_detail (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
deleted_at timestamptz,
deleted_by varchar(100),
id varchar(120) not null primary key,
name varchar(100),
abbr varchar(50),
region varchar(10),
address varchar(120),
contact varchar(100),
phone varchar(50),
unit_service_fee numeric(8,2) not null default 0,
service_expiration date not null
);
create table if not exists user_charge (
created_at timestamptz not null,
seq bigserial not null primary key,
user_id varchar(120) not null,
fee numeric(12,2),
discount numeric(5,4),
amount numeric(12,2),
charge_to date not null,
settled boolean not null default false,
settled_at timestamptz,
cancelled boolean not null default false,
cancelled_at timestamptz,
refunded boolean not null default false,
refunded_at timestamptz
);
create table if not exists park (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
deleted_at timestamptz,
deleted_by varchar(100),
id varchar(120) not null primary key,
user_id varchar(120) not null,
name varchar(70) not null,
abbr varchar(50),
area numeric(14,2),
tenement_quantity numeric(8,0),
capacity numeric(16,2),
category smallint not null default 0,
meter_04kv_type smallint not null default 0,
region varchar(10),
address varchar(120),
contact varchar(100),
phone varchar(50),
enabled boolean not null default true
);
create table if not exists meter_04kv (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
code varchar(120) not null,
park_id varchar(120) not null,
address varchar(100),
customer_name varchar(100),
contact_name varchar(70),
contact_phone varchar(50),
ratio numeric(8,4) not null default 1,
seq bigint not null default 0,
public_meter boolean not null default false,
dilute boolean not null default false,
enabled boolean not null default true,
primary key (code, park_id)
);
create table if not exists maintenance_fee (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
deleted_at timestamptz,
deleted_by varchar(100),
id varchar(120) not null primary key,
park_id varchar(120) not null,
name varchar(50) not null,
fee numeric(8,2) not null default 0,
memo text,
enabled boolean not null default true
);
create table if not exists report (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
id varchar(120) not null primary key,
park_id varchar(120) not null,
period date not null,
category smallint not null default 0,
meter_04kv_type smallint not null default 0,
step_state jsonb not null,
published boolean not null default false,
published_at timestamptz,
withdraw smallint not null default 0,
last_withdraw_applied_at timestamptz,
last_withdraw_audit_at timestamptz
);
create table if not exists report_summary (
report_id varchar(120) not null primary key,
overall numeric(14,2) not null default 0,
overall_fee numeric(14,2) not null default 0,
consumption_fee numeric(14,2),
overall_price numeric(16,8),
critical numeric(14,2) not null default 0,
critical_fee numeric(14,2) not null default 0,
critical_price numeric(16,8),
peak numeric(14,2) not null default 0,
peak_fee numeric(14,2) not null default 0,
peak_price numeric(16,8),
flat numeric(14,2) not null default 0,
flat_fee numeric(14,2) not null default 0,
flat_price numeric(16,8),
valley numeric(14,2) not null default 0,
valley_fee numeric(14,2) not null default 0,
valley_price numeric(16,8),
loss numeric(14,2),
loss_fee numeric(16,2),
loss_proportion numeric(16,15),
customer_consumption numeric(16,2),
customer_consumption_fee numeric(14,2),
customer_consumption_critical numeric(16,2),
customer_consumption_critical_fee numeric(14,2),
customer_consumption_peak numeric(16,2),
customer_consumption_peak_fee numeric(14,2),
customer_consumption_flat numeric(16,2),
customer_consumption_flat_fee numeric(14,2),
customer_consumption_valley numeric(16,2),
customer_consumption_valley_fee numeric(14,2),
public_consumption numeric(16,2),
public_consumption_fee numeric(14,2),
public_consumption_proportion numeric(16,15),
public_consumption_critical numeric(16,2),
public_consumption_critical_fee numeric(14,2),
public_consumption_peak numeric(16,2),
public_consumption_peak_fee numeric(14,2),
public_consumption_flat numeric(16,2),
public_consumption_flat_fee numeric(14,2),
public_consumption_valley numeric(16,2),
public_consumption_valley_fee numeric(14,2),
basic_fee numeric(14,2) not null default 0,
basic_diluted_price numeric(18,8),
adjust_fee numeric(14,2) not null default 0,
adjust_diluted_price numeric(18,8),
maintenance_diluted_price numeric(16,8),
loss_diluted_price numeric(16,8),
public_consumption_diluted_price numeric(16,8),
maintenance_overall numeric(16,8),
final_diluted_overall numeric(14,2)
);
create table if not exists will_diluted_fee (
id varchar(120) not null primary key,
report_id varchar(120) not null,
source_id varchar(120),
name varchar(50) not null,
fee numeric(8,2) not null default 0,
memo text
);
create table if not exists end_user_detail (
created_at timestamptz not null,
created_by varchar(100),
last_modified_at timestamptz,
last_modified_by varchar(100),
report_id varchar(120) not null,
park_id varchar(120) not null,
meter_04kv_id varchar(120) not null,
seq bigint not null default 0,
ratio numeric(8,4) not null default 1,
address varchar(100),
customer_name varchar(100),
contact_name varchar(70),
contact_phone varcahar(50),
public_meter boolean not null default false,
dilute boolean not null default false,
last_period_overall numeric(14,2) not null default 0,
last_period_critical numeric(14,2) not null default 0,
last_period_peak numeric(14,2) not null default 0,
last_period_flat numeric(14,2) not null default 0,
last_period_valley numeric(14,2) not null default 0,
current_period_overall numeric(14,2) not null default 0,
current_period_critical numeric(14,2) not null default 0,
current_period_peak numeric(14,2) not null default 0,
current_period_flat numeric(14,2) not null default 0,
current_period_valley numeric(14,2) not null default 0,
adjust_overall numeric(14,2) not null default 0,
adjust_critical numeric(14,2) not null default 0,
adjust_peak numeric(14,2) not null default 0,
adjust_flat numeric(14,2) not null default 0,
adjust_valley numeric(14,2) not null default 0,
overall numeric(14,2),
overall_fee numeric(14,2),
overall_proportion numeric(16,15) not null default 0,
critical numeric(14,2),
critical_fee numeric(18,8),
peak numeric(14,2),
peak_fee numeric(18,8),
flat numeric(14,2),
flat_fee numeric(18,8),
valley numeric(14,2),
valley_fee numeric(18,8),
basic_fee_diluted numeric(18,8),
adjust_fee_diluted numeric(18,8),
loss_diluted numeric(18,8),
loss_fee_diluted numeric(18,8),
maintenance_fee_diluted numeric(18,8),
public_consumption_diluted numeric(18,8),
final_diluted numeric(14,2),
final_charge numeric(14,2),
primary key (report_id, park_id, meter_04kv_id)
);

View File

@@ -0,0 +1,21 @@
drop table if exists region;
drop table if exists user;
drop table if exists user_detail;
drop table if exists user_charge;
drop table if exists park;
drop table if exists meter_04kv;
drop table if exists maintenance_fee;
drop table if exists report;
drop table if exists report_summary;
drop table if exists will_diluted_fee;
drop table if exists end_user_detail;

View File

@@ -0,0 +1,31 @@
alter table if exists `user` add constraint user_type_check check (type in (0, 1, 2));
alter table if exists user_detail add constraint positive_service_fee check (unit_service_fee >= 0);
alter table if exists user_charge add constraint positive_fee check (fee >= 0);
alter table if exists user_charge add constraint positive_amount check (amount >= 0);
alter table if exists park add constraint positive_tenement check (tenement_quantity >= 0);
alter table if exists park add constraint positive_area check (area >= 0);
alter table if exists park add constraint positive_capacity check (capacity >= 0);
alter table if exists park add constraint category_check check (category in (0, 1, 2));
alter table if exists park add constraint meter_check check (meter_04kv_type in (0, 1));
alter table if exists meter_04kv add constraint positive_ratio check (ratio > 0);
alter table if exists maintenance_fee add constraint positive_fee check (fee >= 0);
alter table if exists report add constraint category_check check (category in (0, 1, 2));
alter table if exists report add constraint meter_check check (meter_04kv_type in (0, 1));
alter table if exists report add constraint withdraw_action_check check (withdraw in (0, 1, 2, 3));
alter table if exists will_diluted_fee add constraint positive_fee check (fee >= 0);
alter table if exists end_user_detail add constraint positive_ratio check (ratio > 0);

View File

@@ -0,0 +1,31 @@
alter table if exists user drop constraint user_type_check;
alter table if exists user_detail drop constraint positive_service_fee;
alter table if exists user_charge drop constraint positive_fee;
alter table if exists user_charge drop constraint positive_amount;
alter table if exists park drop constraint positive_tenement;
alter table if exists park drop constraint positive_area;
alter table if exists park drop constraint positive_capacity;
alter table if exists park drop constraint category_check;
alter table if exists park drop constraint meter_check;
alter table if exists meter_04kv drop constraint positive_ratio;
alter table if exists maintenance_fee drop constraint positive_fee;
alter table if exists report drop constraint category_check;
alter table if exists report drop constraint meter_check;
alter table if exists report drop constraint withdraw_action_check;
alter table if exists will_diluted_fee drop constraint positive_fee;
alter table if exists end_user_detail drop constraint positive_ratio;

View File

@@ -0,0 +1,23 @@
update report_summary
set
customer_consumption = nullif(trim(customers->>'consumption'), '')::numeric(16,2),
customer_consumption_fee = nullif(trim(customers->>'consumptionFee'), '')::numeric(14,2),
customer_consumption_critical = nullif(trim(customers->>'critical'), '')::numeric(16,2),
customer_consumption_critical_fee = nullif(trim(customers->>'criticalFee'), '')::numeric(14,2),
customer_consumption_peak = nullif(trim(customers->>'peak'), '')::numeric(16,2),
customer_consumption_peak_fee = nullif(trim(customers->>'peakFee'), '')::numeric(14,2),
customer_consumption_flat = nullif(trim(customers->>'flat'), '')::numeric(16,2),
customer_consumption_flat_fee = nullif(trim(customers->>'flatFee'), '')::numeric(14,2),
customer_consumption_valley = nullif(trim(customers->>'valley'), '')::numeric(16,2),
customer_consumption_valley_fee = nullif(trim(customers->>'valleyFee'), '')::numeric(14,2),
public_consumption = nullif(trim(publics->>'consumption'), '')::numeric(16,2),
public_consumption_fee = nullif(trim(publics->>'consumptionFee'), '')::numeric(14,2),
public_consumption_critical = nullif(trim(publics->>'critical'), '')::numeric(16,2),
public_consumption_critical_fee = nullif(trim(publics->>'criticalFee'), '')::numeric(14,2),
public_consumption_peak = nullif(trim(publics->>'peak'), '')::numeric(16,2),
public_consumption_peak_fee = nullif(trim(publics->>'peakFee'), '')::numeric(14,2),
public_consumption_flat = nullif(trim(publics->>'flat'), '')::numeric(16,2),
public_consumption_flat_fee = nullif(trim(publics->>'flatFee'), '')::numeric(14,2),
public_consumption_valley = nullif(trim(publics->>'valley'), '')::numeric(16,2),
public_consumption_valley_fee = nullif(trim(publics->>'valleyFee'), '')::numeric(14,2),
public_consumption_proportion = nullif(trim(publics->>'proportion'), '')::numeric(16,15);

View File

@@ -0,0 +1,46 @@
alter table report_summary
add column if not exists customers jsonb,
add column if not exists publics jsonb,
add column if not exists diluteds jsonb;
update report_summary
set
customers = jsonb_build_object(
'consumption', customer_consumption,
'fee', customer_consumption_fee,
'critical', customer_consumption_critical,
'criticalFee', customer_consumption_critical_fee,
'peak', customer_consumption_peak,
'peakFee', customer_consumption_peak_fee,
'flat', customer_consumption_flat,
'flatFee', customer_consumption_flat_fee,
'valley', customer_consumption_valley,
'valleyFee', customer_consumption_valley_fee,
'proportion', null
),
diluteds = jsonb_build_object(
'consumption', public_consumption,
'fee', public_consumption_fee,
'critical', public_consumption_critical,
'criticalFee', public_consumption_critical_fee,
'peak', public_consumption_peak,
'peakFee', public_consumption_peak_fee,
'flat', public_consumption_flat,
'flatFee', public_consumption_flat_fee,
'valley', public_consumption_valley,
'valleyFee', public_consumption_valley_fee,
'proportion', public_consumption_proportion
),
publics = jsonb_build_object(
'consumption', null,
'fee', null,
'critical', null,
'criticalFee', null,
'peak', null,
'peakFee', null,
'flat', null,
'flatFee', null,
'valley', null,
'valleyFee', null,
'proportion', null
);

View File

@@ -0,0 +1,15 @@
alter table meter_04kv
add column dilute boolean not null default false;
alter table end_user_detail
add column dilute boolean not null default false,
add column maintenance_fee_diluted numeric(18,8),
add column public_consumption_diluted numeric(18,8);
alter table maintenance_fee
drop column period;
alter table report_summary
drop column authorize_loss,
drop column authorize_loss_fee,
drop column authorize_loss_proportion;

View File

@@ -0,0 +1,37 @@
alter table meter_04kv
drop column dilute;
alter table end_user_detail
drop column dilute,
drop column maintenance_fee_diluted,
drop column public_consumption_diluted;
alter table maintenance_fee
add column period varchar(10);
alter table report_summary
drop column customer_consumption,
drop column customer_consumption_fee,
drop column customer_consumption_critical,
drop column customer_consumption_critical_fee,
drop column customer_consumption_peak,
drop column customer_consumption_peak_fee,
drop column customer_consumption_flat,
drop column customer_consumption_flat_fee,
drop column customer_consumption_valley,
drop column customer_consumption_valley_fee,
drop column public_consumption,
drop column public_consumption_fee,
drop column public_consumption_critical,
drop column public_consumption_critical_fee,
drop column public_consumption_peak,
drop column public_consumption_peak_fee,
drop column public_consumption_flat,
drop column public_consumption_flat_fee,
drop column public_consumption_valley,
drop column public_consumption_valley_fee,
drop column public_consumption_proportion,
drop column diluteds,
add column authorize_loss numeric(14,2),
add column authorize_loss_fee numeric(16,2),
add column authorize_loss_proportion numeric(16,15);

21
migration/main.go Normal file
View File

@@ -0,0 +1,21 @@
package migration
import (
"electricity_bill_calc/logger"
"embed"
"github.com/uptrace/bun/migrate"
"go.uber.org/zap"
)
var (
//go:embed *.sql
sqlMigrations embed.FS
Migrations = migrate.NewMigrations()
)
func init() {
if err := Migrations.Discover(sqlMigrations); err != nil {
logger.Named("Migrations").Fatal("Unable to load migrations.", zap.Error(err))
}
}

View File

@@ -1,62 +1,77 @@
package model package model
import ( import (
"context"
"errors" "errors"
"time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type EndUserDetail struct { type EndUserDetail struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:end_user_detail,alias:eud"`
ReportId string `xorm:"varchar(120) pk not null" json:"reportId"` CreatedAndModified `bun:"extend"`
ParkId string `xorm:"varchar(120) pk not null" json:"parkId"` ReportId string `bun:",pk,notnull" json:"reportId"`
MeterId string `xorm:"meter_04kv_id varchar(120) pk not null" json:"meterId"` ParkId string `bun:",pk,notnull" json:"parkId"`
Seq int64 `xorm:"bigint not null default 0" json:"seq"` MeterId string `bun:"meter_04kv_id,pk,notnull" json:"meterId"`
Ratio decimal.Decimal `xorm:"numeric(8,4) not null default 1" json:"ratio"` Seq int64 `bun:"type:bigint,notnull" json:"seq"`
Address *string `xorm:"varchar(100)" json:"address"` Ratio decimal.Decimal `bun:"type:numeric,notnull" json:"ratio"`
CustomerName *string `xorm:"varchar(100)" json:"customerName"` Address *string `json:"address"`
ContactName *string `xorm:"varchar(70)" json:"contactName"` CustomerName *string `json:"customerName"`
ContactPhone *string `xorm:"varchar(50)" json:"contactPhone"` ContactName *string `json:"contactName"`
IsPublicMeter bool `xorm:"'public_meter' bool not null default false" json:"isPublicMeter"` ContactPhone *string `json:"contactPhone"`
WillDilute bool `xorm:"'dilute' bool not null default false" json:"willDilute"` IsPublicMeter bool `bun:"public_meter,notnull" json:"isPublicMeter"`
LastPeriodOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodOverall"` LastPeriodOverall decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodOverall"`
LastPeriodCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodCritical"` LastPeriodCritical decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodCritical"`
LastPeriodPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodPeak"` LastPeriodPeak decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodPeak"`
LastPeriodFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodFlat"` LastPeriodFlat decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodFlat"`
LastPeriodValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"lastPeriodValley"` LastPeriodValley decimal.Decimal `bun:"type:numeric,notnull" json:"lastPeriodValley"`
CurrentPeriodOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodOverall"` CurrentPeriodOverall decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodOverall"`
CurrentPeriodCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodCritical"` CurrentPeriodCritical decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodCritical"`
CurrentPeriodPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodPeak"` CurrentPeriodPeak decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodPeak"`
CurrentPeriodFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodFlat"` CurrentPeriodFlat decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodFlat"`
CurrentPeriodValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"currentPeriodValley"` CurrentPeriodValley decimal.Decimal `bun:"type:numeric,notnull" json:"currentPeriodValley"`
AdjustOverall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustOverall"` AdjustOverall decimal.Decimal `bun:"type:numeric,notnull" json:"adjustOverall"`
AdjustCritical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustCritical"` AdjustCritical decimal.Decimal `bun:"type:numeric,notnull" json:"adjustCritical"`
AdjustPeak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustPeak"` AdjustPeak decimal.Decimal `bun:"type:numeric,notnull" json:"adjustPeak"`
AdjustFlat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustFlat"` AdjustFlat decimal.Decimal `bun:"type:numeric,notnull" json:"adjustFlat"`
AdjustValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustValley"` AdjustValley decimal.Decimal `bun:"type:numeric,notnull" json:"adjustValley"`
Overall decimal.NullDecimal `xorm:"numeric(14,2)" json:"overall"` Overall decimal.NullDecimal `bun:"type:numeric" json:"overall"`
OverallFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"overallFee"` OverallFee decimal.NullDecimal `bun:"type:numeric" json:"overallFee"`
OverallProportion decimal.Decimal `xorm:"numeric(16,15) not null default 0" json:"-"` OverallProportion decimal.Decimal `bun:"type:numeric,notnull" json:"-"`
Critical decimal.NullDecimal `xorm:"numeric(14,2)" json:"critical"` Critical decimal.NullDecimal `bun:"type:numeric" json:"critical"`
CriticalFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"criticalFee"` CriticalFee decimal.NullDecimal `bun:"type:numeric" json:"criticalFee"`
Peak decimal.NullDecimal `xorm:"numeric(14,2)" json:"peak"` Peak decimal.NullDecimal `bun:"type:numeric" json:"peak"`
PeakFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"peakFee"` PeakFee decimal.NullDecimal `bun:"type:numeric" json:"peakFee"`
Flat decimal.NullDecimal `xorm:"numeric(14,2)" json:"flat"` Flat decimal.NullDecimal `bun:"type:numeric" json:"flat"`
FlatFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"flatFee"` FlatFee decimal.NullDecimal `bun:"type:numeric" json:"flatFee"`
Valley decimal.NullDecimal `xorm:"numeric(14,2)" json:"valley"` Valley decimal.NullDecimal `bun:"type:numeric" json:"valley"`
ValleyFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"valleyFee"` ValleyFee decimal.NullDecimal `bun:"type:numeric" json:"valleyFee"`
BasicFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"basicFeeDiluted"` BasicFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"basicFeeDiluted"`
AdjustFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"adjustFeeDiluted"` AdjustFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"adjustFeeDiluted"`
LossDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"lossDiluted"` LossDiluted decimal.NullDecimal `bun:"type:numeric" json:"lossDiluted"`
LossFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"lossFeeDiluted"` LossFeeDiluted decimal.NullDecimal `bun:"type:numeric" json:"lossFeeDiluted"`
MaintenanceFeeDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"maintenanceFeeDiluted"` FinalDiluted decimal.NullDecimal `bun:"type:numeric" json:"finalDiluted"`
PublicConsumptionDiluted decimal.NullDecimal `xorm:"numeric(18,8)" json:"publicConsumptionDiluted"` FinalCharge decimal.NullDecimal `bun:"type:numeric" json:"finalCharge"`
FinalDiluted decimal.NullDecimal `xorm:"numeric(14,2)" json:"finalDiluted"` Initialize bool `bun:"-" json:"-"`
FinalCharge decimal.NullDecimal `xorm:"numeric(14,2)" json:"finalCharge"` Origin *Meter04KV `bun:"rel:belongs-to,join:park_id=park_id,join:meter_04kv_id=code" json:"-"`
Report *Report `bun:"rel:belongs-to,join:report_id=id" json:"-"`
Park *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"`
} }
func (EndUserDetail) TableName() string { var _ bun.BeforeAppendModelHook = (*EndUserDetail)(nil)
return "end_user_detail"
func (d *EndUserDetail) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
d.CreatedAt = oprTime
d.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
d.LastModifiedAt = &oprTime
}
return nil
} }
func (d EndUserDetail) Validate() (bool, error) { func (d EndUserDetail) Validate() (bool, error) {
@@ -72,6 +87,7 @@ func (d EndUserDetail) Validate() (bool, error) {
} }
func (d *EndUserDetail) CalculatePeriod() { func (d *EndUserDetail) CalculatePeriod() {
d.LastPeriodFlat = d.LastPeriodOverall.Sub(d.LastPeriodCritical).Sub(d.LastPeriodPeak).Sub(d.LastPeriodValley)
d.CurrentPeriodFlat = d.CurrentPeriodOverall.Sub(d.CurrentPeriodCritical).Sub(d.CurrentPeriodPeak).Sub(d.CurrentPeriodValley) d.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.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.Critical = decimal.NewNullDecimal(d.CurrentPeriodCritical.Sub(d.LastPeriodCritical).Mul(d.Ratio).Add(d.AdjustCritical).RoundBank(2))
@@ -82,7 +98,11 @@ func (d *EndUserDetail) CalculatePeriod() {
type EndUserImport struct { type EndUserImport struct {
MeterId string `excel:"meterId"` MeterId string `excel:"meterId"`
LastPeriodOverall decimal.Decimal `excel:"lastPeriodOverall"`
CurrentPeriodOverall decimal.Decimal `excel:"currentPeriodOverall"` CurrentPeriodOverall decimal.Decimal `excel:"currentPeriodOverall"`
LastPeriodCritical decimal.NullDecimal `excel:"lastPeriodCritical"`
LastPeriodPeak decimal.NullDecimal `excel:"lastPeriodPeak"`
LastPeriodValley decimal.NullDecimal `excel:"lastPeriodValley"`
CurrentPeriodCritical decimal.NullDecimal `excel:"currentPeriodCritical"` CurrentPeriodCritical decimal.NullDecimal `excel:"currentPeriodCritical"`
CurrentPeriodPeak decimal.NullDecimal `excel:"currentPeriodPeak"` CurrentPeriodPeak decimal.NullDecimal `excel:"currentPeriodPeak"`
CurrentPeriodValley decimal.NullDecimal `excel:"currentPeriodValley"` CurrentPeriodValley decimal.NullDecimal `excel:"currentPeriodValley"`
@@ -92,3 +112,22 @@ type EndUserImport struct {
AdjustFlat decimal.NullDecimal `excel:"adjustFlat"` AdjustFlat decimal.NullDecimal `excel:"adjustFlat"`
AdjustValley decimal.NullDecimal `excel:"adjustValley"` AdjustValley decimal.NullDecimal `excel:"adjustValley"`
} }
type EndUserPeriodStat struct {
CustomerName string `json:"customerName"`
Address string `json:"address"`
ParkId string `json:"parkId"`
MeterId string `bun:"meter_04kv_id" json:"meterId"`
IsPublicMeter bool `bun:"public_meter" json:"isPublicMeter"`
Kind int8 `bun:"-" json:"pvKind"`
Overall decimal.NullDecimal `json:"overall"`
Critical decimal.NullDecimal `json:"critical"`
Peak decimal.NullDecimal `json:"peak"`
Valley decimal.NullDecimal `json:"valley"`
OverallFee decimal.NullDecimal `json:"overallFee"`
CriticalFee decimal.NullDecimal `json:"criticalFee"`
PeakFee decimal.NullDecimal `json:"peakFee"`
ValleyFee decimal.NullDecimal `json:"valleyFee"`
AdjustFee decimal.NullDecimal `bun:"final_diluted" json:"adjustFee"`
AdjustProportion decimal.NullDecimal `bun:"-" json:"adjustProportion"`
}

View File

@@ -1,20 +1,48 @@
package model package model
import ( import (
"context"
"time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type MaintenanceFee struct { type MaintenanceFee struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:maintenance_fee,alias:m"`
Deleted `xorm:"extends"` CreatedAndModified `bun:"extend"`
Id string `xorm:"varchar(120) pk not null" json:"id"` Deleted `bun:"extend"`
ParkId string `xorm:"varchar(120) not null" json:"parkId"` Id string `bun:",pk,notnull" json:"id"`
Name string `xorm:"varchar(50) not null" json:"name"` ParkId string `bun:",notnull" json:"parkId"`
Fee decimal.Decimal `xorm:"numeric(8,2) not null" json:"fee"` Name string `bun:",notnull" json:"name"`
Memo *string `xorm:"text" json:"memo"` Period string `bun:",notnull" json:"period"`
Enabled bool `xorm:"bool not null" json:"enabled"` Fee decimal.Decimal `bun:"type:numeric,notnull" json:"fee"`
Memo *string `bun:"type:text" json:"memo"`
Enabled bool `bun:",notnull" json:"enabled"`
Park Park `bun:"rel:belongs-to,join:park_id=id"`
} }
func (MaintenanceFee) TableName() string { var _ bun.BeforeAppendModelHook = (*MaintenanceFee)(nil)
return "maintenance_fee"
func (f *MaintenanceFee) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
f.CreatedAt = oprTime
f.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
f.LastModifiedAt = &oprTime
}
return nil
}
type AdditionalCharge struct {
ParkId string `json:"parkId"`
Period string `json:"period"`
Fee decimal.Decimal `json:"fee"`
Price decimal.Decimal `json:"price"`
QuarterPrice decimal.Decimal `json:"quarterPrice"`
SemiAnnualPrice decimal.Decimal `json:"semiAnnualPrice"`
Enterprise UserDetailSimplified `json:"user"`
Park Park `json:"park"`
} }

View File

@@ -1,24 +1,39 @@
package model package model
import ( import (
"context"
"time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type Meter04KV struct { type Meter04KV struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:meter_04kv,alias:mt"`
Code string `xorm:"varchar(120) pk not null" json:"code" excel:"code"` CreatedAndModified `bun:"extend"`
ParkId string `xorm:"varchar(120) pk not null" json:"parkId"` Code string `bun:",pk,notnull" json:"code" excel:"code"`
Address *string `xorm:"varchar(100)" json:"address" excel:"address"` ParkId string `bun:",pk,notnull" json:"parkId"`
CustomerName *string `xorm:"varchar(100)" json:"customerName" excel:"name"` Address *string `json:"address" excel:"address"`
ContactName *string `xorm:"varchar(70)" json:"contactName" excel:"contact"` CustomerName *string `json:"customerName" excel:"name"`
ContactPhone *string `xorm:"varchar(50)" json:"contactPhone" excel:"phone"` ContactName *string `json:"contactName" excel:"contact"`
Ratio decimal.Decimal `xorm:"numeric(8,4) not null default 1" json:"ratio" excel:"ratio"` ContactPhone *string `json:"contactPhone" excel:"phone"`
Seq int64 `xorm:"bigint not null" json:"seq" excel:"seq"` Ratio decimal.Decimal `bun:"type:numeric,notnull" json:"ratio" excel:"ratio"`
IsPublicMeter bool `xorm:"'public_meter' bool not null default false" json:"isPublicMeter" excel:"public"` Seq int64 `bun:"type:bigint,notnull" json:"seq" excel:"seq"`
WillDilute bool `xorm:"'dilute' bool not null default false" json:"willDilute" excel:"dilute"` IsPublicMeter bool `bun:"public_meter,notnull" json:"isPublicMeter" excel:"public"`
Enabled bool `xorm:"bool not null default true" json:"enabled"` Enabled bool `bun:",notnull" json:"enabled"`
ParkDetail *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"`
} }
func (Meter04KV) TableName() string { var _ bun.BeforeAppendModelHook = (*Meter04KV)(nil)
return "meter_04kv"
func (m *Meter04KV) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
m.CreatedAt = oprTime
m.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
m.LastModifiedAt = &oprTime
}
return nil
} }

View File

@@ -1,9 +1,12 @@
package model package model
import ( import (
"context"
"time" "time"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
const ( const (
@@ -18,50 +21,69 @@ const (
) )
type Park struct { type Park struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:park,alias:p"`
Deleted `xorm:"extends"` CreatedAndModified `bun:"extend"`
Id string `xorm:"varchar(120) pk not null" json:"id"` Deleted `bun:"extend"`
UserId string `xorm:"varchar(120) not null" json:"userId"` Id string `bun:",pk,notnull" json:"id"`
Name string `xorm:"varchar(70) not null" json:"name"` UserId string `bun:",notnull" json:"userId"`
Abbr *string `xorm:"varchar(50)" json:"abbr"` Name string `bun:",notnull" json:"name"`
Area decimal.NullDecimal `xorm:"numeric(14,2)" json:"area"` Abbr *string `json:"abbr"`
TenementQuantity decimal.NullDecimal `xorm:"numeric(8,0)" json:"tenement"` Area decimal.NullDecimal `bun:"type:numeric" json:"area"`
Capacity decimal.NullDecimal `xorm:"numeric(16,2)" json:"capacity"` TenementQuantity decimal.NullDecimal `bun:"type:numeric" json:"tenement"`
Category int8 `xorm:"smallint not null default 0" json:"category"` Capacity decimal.NullDecimal `bun:"type:numeric" json:"capacity"`
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null default 0" json:"meter04kvType"` Category int8 `bun:"type:smallint,notnull" json:"category"`
Region *string `xorm:"varchar(10)" json:"region"` SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"`
Address *string `xorm:"varchar(120)" json:"address"` Region *string `json:"region"`
Contact *string `xorm:"varchar(100)" json:"contact"` Address *string `json:"address"`
Phone *string `xorm:"varchar(50)" json:"phone"` Contact *string `json:"contact"`
Enabled bool `xorm:"bool not null" json:"enabled"` Phone *string `json:"phone"`
} Enabled bool `bun:",notnull" json:"enabled"`
EnterpriseIndex *User `bun:"rel:belongs-to,join:user_id=id" json:"-"`
func (Park) TableName() string { Enterprise *UserDetail `bun:"rel:belongs-to,join:user_id=id" json:"-"`
return "park" MaintenanceFees []*MaintenanceFee `bun:"rel:has-many,join:id=park_id" json:"-"`
Meters []*Meter04KV `bun:"rel:has-many,join:id=park_id" json:"-"`
Reports []*Report `bun:"rel:has-many,join:id=park_id" json:"-"`
} }
type ParkSimplified struct { type ParkSimplified struct {
Id string `xorm:"varchar(120) pk not null" json:"id"` bun.BaseModel `bun:"table:park,alias:p"`
UserId string `xorm:"varchar(120) not null" json:"userId"` Id string `bun:",pk,notnull" json:"id"`
Name string `xorm:"varchar(70) not null" json:"name"` UserId string `bun:",notnull" json:"userId"`
Abbr *string `xorm:"varchar(50)" json:"abbr"` Name string `bun:",notnull" json:"name"`
Area decimal.NullDecimal `xorm:"numeric(14,2)" json:"area"` Abbr *string `json:"abbr"`
TenementQuantity decimal.NullDecimal `xorm:"numeric(8,0)" json:"tenement"` Area decimal.NullDecimal `json:"area"`
Capacity decimal.NullDecimal `xorm:"numeric(16,2)" json:"capacity"` TenementQuantity decimal.NullDecimal `json:"tenement"`
Category int8 `xorm:"smallint not null" json:"category"` Capacity decimal.NullDecimal `json:"capacity"`
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null" json:"meter04kvType"` Category int8 `bun:"type:smallint,notnull" json:"category"`
Region *string `xorm:"varchar(10)" json:"region"` SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"`
Address *string `xorm:"varchar(120)" json:"address"` Region *string `json:"region"`
Contact *string `xorm:"varchar(100)" json:"contact"` Address *string `json:"address"`
Phone *string `xorm:"varchar(50)" json:"phone"` Contact *string `json:"contact"`
} Phone *string `json:"phone"`
func (ParkSimplified) TableName() string {
return "park"
} }
type ParkPeriodStatistics struct { type ParkPeriodStatistics struct {
Id string `xorm:"varchar(120) not null" json:"id"` Id string `bun:"park__id,notnull" json:"id"`
Name string `xorm:"varchar(120) not null" json:"name"` Name string `bun:"park__name,notnull" json:"name"`
Period *time.Time `xorm:"date" json:"period" time_format:"simple_date" time_location:"shanghai"` Period *Date `bun:"type:date" json:"period"`
}
func FromPark(park Park) ParkSimplified {
dest := ParkSimplified{}
copier.Copy(&dest, park)
return dest
}
var _ bun.BeforeAppendModelHook = (*Park)(nil)
func (p *Park) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
p.CreatedAt = oprTime
p.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
p.LastModifiedAt = &oprTime
}
return nil
} }

View File

@@ -41,7 +41,7 @@ type EndUserOverallPart struct {
ValleyFee decimal.NullDecimal `json:"valleyFee"` ValleyFee decimal.NullDecimal `json:"valleyFee"`
} }
type PublicConsumptionOverallPart struct { type ConsumptionOverallPart struct {
Overall decimal.Decimal `json:"overall"` Overall decimal.Decimal `json:"overall"`
OverallPrice decimal.Decimal `json:"overallPrice"` OverallPrice decimal.Decimal `json:"overallPrice"`
ConsumptionFee decimal.Decimal `json:"consumptionFee"` ConsumptionFee decimal.Decimal `json:"consumptionFee"`
@@ -66,29 +66,31 @@ type LossPart struct {
Price decimal.Decimal `json:"price"` Price decimal.Decimal `json:"price"`
ConsumptionFee decimal.Decimal `json:"consumptionFee"` ConsumptionFee decimal.Decimal `json:"consumptionFee"`
Proportion decimal.Decimal `json:"proportion"` Proportion decimal.Decimal `json:"proportion"`
AuthorizeQuantity decimal.Decimal `json:"authorizeQuantity"`
AuthorizeConsumptionFee decimal.Decimal `json:"authorizeConsumptionFee"`
} }
type OtherShouldCollectionPart struct { type OtherShouldCollectionPart struct {
MaintenanceFee decimal.NullDecimal `json:"maintenanceFee"` LossFee decimal.NullDecimal `json:"lossFee"`
BasicFees decimal.Decimal `json:"basicFees"` BasicFees decimal.Decimal `json:"basicFees"`
} }
type MaintenancePart struct { type MaintenancePart struct {
BasicFees decimal.Decimal `json:"basicFees"` BasicFees decimal.Decimal `json:"basicFees"`
LossFee decimal.Decimal `json:"lossFee"` LossFee decimal.Decimal `json:"lossFee"`
PublicConsumptionFee decimal.Decimal `json:"publicConsumptionFee"` AdjustFee decimal.Decimal `json:"adjustFee"`
MaintenanceFee decimal.Decimal `json:"maintenanceFee"` LossProportion decimal.Decimal `json:"lossProportion"`
FinalMaintenance decimal.Decimal `json:"finalMaintenance"` AdjustProportion decimal.Decimal `json:"adjustProportion"`
MaintenanceProportion decimal.Decimal `json:"maintenanceProportion"` AdjustPrice decimal.Decimal `json:"adjustPrice"`
MaintenancePrice decimal.Decimal `json:"maintenancePrice"`
PriceRatio decimal.Decimal `json:"priceRatio"`
} }
type EndUserSummary struct { type EndUserSummary struct {
CustomerName *string `json:"customerName"` CustomerName *string `json:"customerName"`
Address *string `json:"address"` Address *string `json:"address"`
MeterId string `json:"meterId"` MeterId string `json:"meterId"`
IsPublicMeter bool `json:"isPublicMeter"`
Overall decimal.Decimal `json:"overall"` Overall decimal.Decimal `json:"overall"`
OverallPrice decimal.Decimal `json:"overallPrice"`
OverallFee decimal.Decimal `json:"overallFee"` OverallFee decimal.Decimal `json:"overallFee"`
Critical decimal.NullDecimal `json:"critical"` Critical decimal.NullDecimal `json:"critical"`
CriticalFee decimal.NullDecimal `json:"criticalFee"` CriticalFee decimal.NullDecimal `json:"criticalFee"`
@@ -96,7 +98,8 @@ type EndUserSummary struct {
PeakFee decimal.NullDecimal `json:"peakFee"` PeakFee decimal.NullDecimal `json:"peakFee"`
Valley decimal.NullDecimal `json:"valley"` Valley decimal.NullDecimal `json:"valley"`
ValleyFee decimal.NullDecimal `json:"valleyFee"` ValleyFee decimal.NullDecimal `json:"valleyFee"`
Maintenance decimal.Decimal `json:"maintenance"` Loss decimal.Decimal `json:"loss"`
LossFee decimal.Decimal `json:"lossFee"`
} }
type Publicity struct { type Publicity struct {
@@ -104,9 +107,9 @@ type Publicity struct {
User UserDetail `json:"enterprise"` User UserDetail `json:"enterprise"`
Park Park `json:"park"` Park Park `json:"park"`
Paid PaidPart `json:"paid"` Paid PaidPart `json:"paid"`
EndUser EndUserOverallPart `json:"endUserSum"` EndUser ConsumptionOverallPart `json:"endUserSum"`
Loss LossPart `json:"loss"` Loss LossPart `json:"loss"`
PublicConsumptionOverall PublicConsumptionOverallPart `json:"public"` PublicConsumptionOverall ConsumptionOverallPart `json:"public"`
OtherCollections OtherShouldCollectionPart `json:"others"` OtherCollections OtherShouldCollectionPart `json:"others"`
Maintenance MaintenancePart `json:"maintenance"` Maintenance MaintenancePart `json:"maintenance"`
EndUserDetails []EndUserSummary `json:"endUser"` EndUserDetails []EndUserSummary `json:"endUser"`

View File

@@ -1,12 +1,11 @@
package model package model
type Region struct { import "github.com/uptrace/bun"
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 { type Region struct {
return "region" bun.BaseModel `bun:"table:region,alias:r"`
Code string `bun:",pk,notnull" json:"code"`
Name string `bun:",notnull" json:"name"`
Level int `bun:",notnull" json:"level"`
Parent string `bun:",notnull" json:"parent"`
} }

View File

@@ -1,6 +1,11 @@
package model package model
import "time" import (
"context"
"time"
"github.com/uptrace/bun"
)
const ( const (
REPORT_NOT_WITHDRAW int8 = iota REPORT_NOT_WITHDRAW int8 = iota
@@ -10,18 +15,23 @@ const (
) )
type Report struct { type Report struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:report,alias:r"`
Id string `xorm:"varchar(120) pk not null" json:"id"` CreatedAndModified `bun:"extend"`
ParkId string `xorm:"varchar(120) not null" json:"parkId"` Id string `bun:",pk,notnull" json:"id"`
Period time.Time `xorm:"date not null" json:"period" time_format:"simple_date" time_location:"shanghai"` ParkId string `bun:",notnull" json:"parkId"`
Category int8 `xorm:"smallint not null default 0" json:"category"` Period time.Time `bun:"type:date,notnull" json:"period" time_format:"simple_date" time_location:"shanghai"`
SubmeterType int8 `xorm:"'meter_04kv_type' smallint not null default 0" json:"meter04kvType"` Category int8 `bun:"type:smallint,notnull" json:"category"`
StepState Steps `xorm:"text not null json" json:"stepState"` SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"`
Published bool `xorm:"bool not null default false" json:"published"` StepState Steps `bun:"type:jsonb,notnull" json:"stepState"`
PublishedAt *time.Time `xorm:"timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"` Published bool `bun:",notnull" json:"published"`
Withdraw int8 `xorm:"smallint not null default 0" json:"withdraw"` PublishedAt *time.Time `bun:"type:timestamptz,nullzero" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"`
LastWithdrawAppliedAt *time.Time `xorm:"timestampz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"` Withdraw int8 `bun:"type:smallint,notnull" json:"withdraw"`
LastWithdrawAuditAt *time.Time `xorm:"timestampz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"` LastWithdrawAppliedAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"`
LastWithdrawAuditAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"`
Park *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"`
Summary *ReportSummary `bun:"rel:has-one,join:id=report_id" json:"-"`
WillDilutedFees []*WillDilutedFee `bun:"rel:has-many,join:id=report_id" json:"-"`
EndUsers []*EndUserDetail `bun:"rel:has-many,join:id=report_id,join:park_id=park_id" json:"-"`
} }
type Steps struct { type Steps struct {
@@ -33,10 +43,6 @@ type Steps struct {
Publish bool `json:"publish"` Publish bool `json:"publish"`
} }
func (Report) TableName() string {
return "report"
}
func NewSteps() Steps { func NewSteps() Steps {
return Steps{ return Steps{
Summary: false, Summary: false,
@@ -49,12 +55,8 @@ func NewSteps() Steps {
} }
type ParkNewestReport struct { type ParkNewestReport struct {
Park Park `xorm:"extends" json:"park"` Park Park `bun:"extends" json:"park"`
Report *Report `xorm:"extends" json:"report"` Report *Report `bun:"extends" json:"report"`
}
func (ParkNewestReport) TableName() string {
return "park"
} }
func (p *ParkNewestReport) AfterLoad() { func (p *ParkNewestReport) AfterLoad() {
@@ -64,27 +66,34 @@ func (p *ParkNewestReport) AfterLoad() {
} }
type ReportIndexSimplified struct { type ReportIndexSimplified struct {
Id string `xorm:"varchar(120) pk not null" json:"id"` bun.BaseModel `bun:"table:report,alias:r"`
ParkId string `xorm:"varchar(120) not null" json:"parkId"` Id string `bun:",pk,notnull" json:"id"`
Period time.Time `xorm:"date not null" json:"period" time_format:"simple_date" time_location:"shanghai"` ParkId string `bun:",notnull" json:"parkId"`
StepState Steps `xorm:"text not null json" json:"stepState"` Period Date `bun:"type:date,notnull" json:"period"`
Published bool `xorm:"bool not null default false" json:"published"` StepState Steps `bun:"type:jsonb,notnull" json:"stepState"`
PublishedAt *time.Time `xorm:"timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"` Published bool `bun:",notnull" json:"published"`
Withdraw int8 `xorm:"smallint not null default 0" json:"withdraw"` PublishedAt *time.Time `bun:"type:timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"`
LastWithdrawAppliedAt *time.Time `xorm:"timestampz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"` Withdraw int8 `bun:"type:smallint,notnull" json:"withdraw"`
LastWithdrawAuditAt *time.Time `xorm:"timestampz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"` LastWithdrawAppliedAt *time.Time `bun:"type:timestamptz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"`
} LastWithdrawAuditAt *time.Time `bun:"type:timestamptz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"`
func (ReportIndexSimplified) TableName() string {
return "report"
} }
type JoinedReportForWithdraw struct { type JoinedReportForWithdraw struct {
Report Report `xorm:"extends" json:"report"` Report Report `bun:"extends" json:"report"`
Park ParkSimplified `xorm:"extends" json:"park"` Park ParkSimplified `bun:"extends" json:"park"`
User UserDetailSimplified `xorm:"extends" json:"user"` User UserDetailSimplified `bun:"extends" json:"user"`
} }
func (JoinedReportForWithdraw) TableName() string { var _ bun.BeforeAppendModelHook = (*Report)(nil)
return "report"
func (p *Report) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
p.CreatedAt = oprTime
p.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
p.LastModifiedAt = &oprTime
}
return nil
} }

View File

@@ -4,62 +4,75 @@ import (
"errors" "errors"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type ReportSummary struct { type ReportSummary struct {
ReportId string `xorm:"varchar(120) pk not null" json:"-"` bun.BaseModel `bun:"table:report_summary,alias:rs"`
Overall decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"overall"` ReportId string `bun:",pk,notnull" json:"-"`
OverallFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"overallFee"` Overall decimal.Decimal `bun:"type:numeric,notnull" json:"overall"`
ConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"consumptionFee"` OverallFee decimal.Decimal `bun:"type:numeric,notnull" json:"overallFee"`
OverallPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"overallPrice"` ConsumptionFee decimal.NullDecimal `bun:"type:numeric" json:"consumptionFee"`
Critical decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"critical"` OverallPrice decimal.NullDecimal `bun:"type:numeric" json:"overallPrice"`
CriticalFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"criticalFee"` Critical decimal.Decimal `bun:"type:numeric,notnull" json:"critical"`
CriticalPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"criticalPrice"` CriticalFee decimal.Decimal `bun:"type:numeric,notnull" json:"criticalFee"`
Peak decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"peak"` CriticalPrice decimal.NullDecimal `bun:"type:numeric" json:"criticalPrice"`
PeakFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"peakFee"` Peak decimal.Decimal `bun:"type:numeric,notnull" json:"peak"`
PeakPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"peakPrice"` PeakFee decimal.Decimal `bun:"type:numeric,notnull" json:"peakFee"`
Flat decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"flat"` PeakPrice decimal.NullDecimal `bun:"type:numeric" json:"peakPrice"`
FlatFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"flatFee"` Flat decimal.Decimal `bun:"type:numeric,notnull" json:"flat"`
FlatPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"flatPrice"` FlatFee decimal.Decimal `bun:"type:numeric,notnull" json:"flatFee"`
Valley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"valley"` FlatPrice decimal.NullDecimal `bun:"type:numeric" json:"flatPrice"`
ValleyFee decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"valleyFee"` Valley decimal.Decimal `bun:"type:numeric,notnull" json:"valley"`
ValleyPrice decimal.NullDecimal `xorm:"numeric(16,8)" json:"valleyPrice"` ValleyFee decimal.Decimal `bun:"type:numeric,notnull" json:"valleyFee"`
Loss decimal.NullDecimal `xorm:"numeric(14,2)" json:"loss"` ValleyPrice decimal.NullDecimal `bun:"type:numeric" json:"valleyPrice"`
LossFee decimal.NullDecimal `xorm:"numeric(16,2)" json:"lossFee"` Loss decimal.NullDecimal `bun:"type:numeric" json:"loss"`
LossProportion decimal.NullDecimal `xorm:"numeric(16,15)" json:"lossProportion"` LossFee decimal.NullDecimal `bun:"type:numeric" json:"lossFee"`
CustomerConsumption decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumption"` LossProportion decimal.NullDecimal `bun:"type:numeric" json:"lossProportion"`
CustomerConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionFee"` AuthorizeLoss decimal.NullDecimal `bun:"type:numeric" json:"authorizeLoss"`
CustomerConsumptionCritical decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionCritical"` AuthorizeLossFee decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossFee"`
CustomerConsumptionCriticalFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionCriticalFee"` AuthorizeLossProportion decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossProportion"`
CustomerConsumptionPeak decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionPeak"` BasicFee decimal.Decimal `bun:"type:numeric,notnull" json:"basicFee"`
CustomerConsumptionPeakFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionPeakFee"` BasicDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"basicDilutedPrice"`
CustomerConsumptionFlat decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionFlat"` AdjustFee decimal.Decimal `bun:"type:numeric,notnull" json:"adjustFee"`
CustomerConsumptionFlatFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionFlatFee"` AdjustDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"adjustDilutedPrice"`
CustomerConsumptionValley decimal.NullDecimal `xorm:"numeric(16,2)" json:"customerConsumptionValley"` MaintenanceDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"maintencanceDilutedPrice"`
CustomerConsumptionValleyFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"customerConsumptionValleyFee"` LossDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"lossDilutedPrice"`
PublicConsumption decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumption"` PublicConsumptionDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"publicConsumptionDilutedPrice"`
PublicConsumptionFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionFee"` MaintenanceOverall decimal.NullDecimal `bun:"type:numeric" json:"maintenanceOverall"`
PublicConsumptionProportion decimal.NullDecimal `xorm:"numeric(16,15)" json:"publicConsumptionProportion"` FinalDilutedOverall decimal.NullDecimal `bun:"type:numeric" json:"finalDilutedOverall"`
PublicConsumptionCritical decimal.NullDecimal `xorm:"numeric(16,2)" json:"publicConsumptionCritical"` Customers Consumptions `bun:"type:jsonb" json:"customers"`
PublicConsumptionCriticalFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"publicConsumptionCriticalFee"` Publics Consumptions `bun:"type:jsonb" json:"publics"`
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 { type Consumptions struct {
return "report_summary" Consumption decimal.NullDecimal `json:"consumption"`
ConsumptionFee decimal.NullDecimal `json:"fee"`
Critical decimal.NullDecimal `json:"critical"`
CriticalFee decimal.NullDecimal `json:"criticalFee"`
Peak decimal.NullDecimal `json:"peak"`
PeakFee decimal.NullDecimal `json:"peakFee"`
Flat decimal.NullDecimal `json:"flat"`
FlatFee decimal.NullDecimal `json:"flatFee"`
Valley decimal.NullDecimal `json:"valley"`
ValleyFee decimal.NullDecimal `json:"valleyFee"`
Proportion decimal.NullDecimal `json:"proportion"`
}
func NewConsumptions() Consumptions {
return Consumptions{
Consumption: decimal.NewNullDecimal(decimal.Zero),
ConsumptionFee: decimal.NewNullDecimal(decimal.Zero),
Critical: decimal.NewNullDecimal(decimal.Zero),
Peak: decimal.NewNullDecimal(decimal.Zero),
PeakFee: decimal.NewNullDecimal(decimal.Zero),
Flat: decimal.NewNullDecimal(decimal.Zero),
CriticalFee: decimal.NewNullDecimal(decimal.Zero),
FlatFee: decimal.NewNullDecimal(decimal.Zero),
Valley: decimal.NewNullDecimal(decimal.Zero),
ValleyFee: decimal.NewNullDecimal(decimal.Zero),
Proportion: decimal.NewNullDecimal(decimal.Zero),
}
} }
func (s ReportSummary) Validate() (bool, error) { func (s ReportSummary) Validate() (bool, error) {
@@ -97,7 +110,7 @@ func (s *ReportSummary) CalculatePrices() {
s.ValleyPrice = decimal.NewNullDecimal(decimal.Zero) s.ValleyPrice = decimal.NewNullDecimal(decimal.Zero)
} }
s.Flat = s.Overall.Sub(s.Critical).Sub(s.Peak).Sub(s.Valley) 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) s.FlatFee = s.ConsumptionFee.Decimal.Sub(s.CriticalFee).Sub(s.PeakFee).Sub(s.ValleyFee)
if s.Flat.GreaterThan(decimal.Zero) { if s.Flat.GreaterThan(decimal.Zero) {
s.FlatPrice = decimal.NewNullDecimal(s.FlatFee.Div(s.Flat).RoundBank(8)) s.FlatPrice = decimal.NewNullDecimal(s.FlatFee.Div(s.Flat).RoundBank(8))
} else { } else {

View File

@@ -3,30 +3,30 @@ package model
import "time" import "time"
type Created struct { type Created struct {
CreatedAt time.Time `xorm:"timestampz not null created" json:"createdAt" time_format:"simple_datetime" time_location:"shanghai"` CreatedAt time.Time `bun:"type:timestamptz,notnull" json:"createdAt" time_format:"simple_datetime" time_location:"shanghai"`
} }
type CreatedWithUser struct { type CreatedWithUser struct {
Created `xorm:"extends"` Created `bun:"extend"`
CreatedBy *string `xorm:"varchar(100)" json:"createdBy"` CreatedBy *string `json:"createdBy"`
} }
type Deleted struct { type Deleted struct {
DeletedAt *time.Time `xorm:"timestampz deleted" json:"deletedAt" time_format:"simple_datetime" time_location:"shanghai"` DeletedAt *time.Time `bun:"type:timestamptz,soft_delete,nullzero" json:"deletedAt" time_format:"simple_datetime" time_location:"shanghai"`
} }
type DeletedWithUser struct { type DeletedWithUser struct {
Deleted `xorm:"extends"` Deleted `bun:"extend"`
DeletedBy *string `xorm:"varchar(120)" json:"deletedBy"` DeletedBy *string `json:"deletedBy"`
} }
type CreatedAndModified struct { type CreatedAndModified struct {
Created `xorm:"extends"` Created `bun:"extend"`
LastModifiedAt *time.Time `xorm:"timestampz updated" json:"lastModifiedAt" time_format:"simple_datetime" time_location:"shanghai"` LastModifiedAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastModifiedAt" time_format:"simple_datetime" time_location:"shanghai"`
} }
type CreatedAndModifiedWithUser struct { type CreatedAndModifiedWithUser struct {
CreatedAndModified `xorm:"extends"` CreatedAndModified `bun:"extend"`
CreatedBy *string `xorm:"varchar(100)" json:"createdBy"` CreatedBy *string `json:"createdBy"`
LastModifiedBy *string `xorm:"varchar(100)" json:"lastModifiedBy"` LastModifiedBy *string `json:"lastModifiedBy"`
} }

118
model/types.go Normal file
View File

@@ -0,0 +1,118 @@
package model
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
"time"
)
type Date struct {
time.Time
}
func NewDate(t time.Time) Date {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
t = t.In(loc)
return Date{
Time: time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc),
}
}
func NewEmptyDate() Date {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
return Date{
Time: time.Time{}.In(loc),
}
}
func ParseDate(t string) (Date, error) {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return NewEmptyDate(), fmt.Errorf("unable to load time zone, %w", err)
}
d, err := time.ParseInLocation("2006-01-02", t, loc)
if err != nil {
return NewEmptyDate(), fmt.Errorf("unable to parse given time, %w", err)
}
return Date{
Time: d,
}, nil
}
func (d Date) IsEmpty() bool {
return d.Time.IsZero()
}
func (d Date) Format(fmt string) string {
return d.Time.Format(fmt)
}
func (d Date) ToString() string {
return d.Time.Format("2006-01-02")
}
var _ driver.Valuer = (*Date)(nil)
func (d Date) Value() (driver.Value, error) {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
return d.In(loc).Format("2006-01-02"), nil
}
var _ sql.Scanner = (*Date)(nil)
// Scan scans the time parsing it if necessary using timeFormat.
func (d *Date) Scan(src interface{}) (err error) {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
panic(err)
}
switch src := src.(type) {
case time.Time:
*d = NewDate(src)
return nil
case string:
d.Time, err = time.ParseInLocation("2006-01-02", src, loc)
return err
case []byte:
d.Time, err = time.ParseInLocation("2006-01-02", string(src), loc)
return err
case nil:
d.Time = time.Time{}
return nil
default:
return fmt.Errorf("unsupported data type: %T", src)
}
}
var _ json.Marshaler = (*Date)(nil)
func (d Date) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Time.Format("2006-01-02"))
}
var _ json.Unmarshaler = (*Date)(nil)
func (d *Date) UnmarshalJSON(raw []byte) error {
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return fmt.Errorf("unable to load time zone, %w", err)
}
var s string
err = json.Unmarshal(raw, &s)
if err != nil {
return fmt.Errorf("unable to unmarshal value, %w", err)
}
d.Time, err = time.ParseInLocation("2006-01-02", s, loc)
return err
}

View File

@@ -1,5 +1,12 @@
package model package model
import (
"context"
"time"
"github.com/uptrace/bun"
)
const ( const (
USER_TYPE_ENT int8 = iota USER_TYPE_ENT int8 = iota
USER_TYPE_SUP USER_TYPE_SUP
@@ -7,29 +14,35 @@ const (
) )
type User struct { type User struct {
Created `xorm:"extends"` bun.BaseModel `bun:"table:user,alias:u"`
Id string `xorm:"varchar(120) pk not null" json:"id"` Created `bun:"extend"`
Username string `xorm:"varchar(30) not null" json:"username"` Id string `bun:",pk,notnull" json:"id"`
Password string `xorm:"varchar(256) not null" json:"-"` Username string `bun:",notnull" json:"username"`
ResetNeeded bool `xorm:"bool not null" json:"resetNeeded"` Password string `bun:",notnull" json:"-"`
Type int8 `xorm:"smallint not null" json:"type"` ResetNeeded bool `bun:",notnull" json:"resetNeeded"`
Enabled bool `xorm:"bool not null" json:"enabled"` Type int8 `bun:"type:smallint,notnull" json:"type"`
} Enabled bool `bun:",notnull" json:"enabled"`
Detail *UserDetail `bun:"rel:has-one,join:id=id" json:"-"`
func (User) TableName() string { Charges []*UserCharge `bun:"rel:has-many,join:id=user_id" json:"-"`
return "user"
} }
type UserWithCredentials struct { type UserWithCredentials struct {
Created `xorm:"extends"` bun.BaseModel `bun:"table:user,alias:u"`
Id string `xorm:"varchar(120) pk not null" json:"id"` Created `bun:"extend"`
Username string `xorm:"varchar(30) not null" json:"username"` Id string `bun:",pk,notnull" json:"id"`
Password string `xorm:"varchar(256) not null" json:"credential"` Username string `bun:",notnull" json:"username"`
ResetNeeded bool `xorm:"bool not null" json:"resetNeeded"` Password string `bun:",notnull" json:"credential"`
Type int8 `xorm:"smallint not null" json:"type"` ResetNeeded bool `bun:",notnull" json:"resetNeeded"`
Enabled bool `xorm:"bool not null" json:"enabled"` Type int8 `bun:"type:smallint,notnull" json:"type"`
Enabled bool `bun:",notnull" json:"enabled"`
} }
func (UserWithCredentials) TableName() string { var _ bun.BeforeAppendModelHook = (*User)(nil)
return "user"
func (u *User) BeforeAppendModel(ctx context.Context, query bun.Query) error {
switch query.(type) {
case *bun.InsertQuery:
u.CreatedAt = time.Now()
}
return nil
} }

View File

@@ -1,36 +1,43 @@
package model package model
import ( import (
"context"
"time" "time"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type UserCharge struct { type UserCharge struct {
Created `xorm:"extends"` bun.BaseModel `bun:"table:user_charge,alias:c"`
Seq int64 `xorm:"bigint pk not null autoincr" json:"seq"` Created `bun:"extend"`
UserId string `xorm:"varchar(120) not null" json:"userId"` Seq int64 `bun:"type:bigint,pk,notnull,autoincrement" json:"seq"`
Fee decimal.NullDecimal `xorm:"numeric(12,2)" json:"fee"` UserId string `bun:",notnull" json:"userId"`
Discount decimal.NullDecimal `xorm:"numeric(5,4)" json:"discount"` Fee decimal.NullDecimal `bun:"type:numeric" json:"fee"`
Amount decimal.NullDecimal `xorm:"numeric(12,2)" json:"amount"` Discount decimal.NullDecimal `bun:"type:numeric" json:"discount"`
ChargeTo time.Time `xorm:"date not null" json:"chargeTo" time_format:"simple_date" time_location:"shanghai"` Amount decimal.NullDecimal `bun:"type:numeric" json:"amount"`
Settled bool `xorm:"bool not null default false" json:"settled"` ChargeTo Date `bun:"type:date,notnull" json:"chargeTo"`
SettledAt *time.Time `xorm:"timestampz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"` Settled bool `bun:",notnull" json:"settled"`
Cancelled bool `xorm:"bool not null default false" json:"cancelled"` SettledAt *time.Time `bun:"type:timestamptz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"`
CancelledAt *time.Time `xorm:"timestampz" json:"cancelledAt" time_format:"simple_datetime" time_location:"shanghai"` Cancelled bool `bun:",notnull" json:"cancelled"`
Refunded bool `xorm:"bool not null default false" json:"refunded"` CancelledAt *time.Time `bun:"type:timestamptz" json:"cancelledAt" time_format:"simple_datetime" time_location:"shanghai"`
RefundedAt *time.Time `xorm:"timestampz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"` Refunded bool `bun:",notnull" json:"refunded"`
} RefundedAt *time.Time `bun:"type:timestamptz" json:"refundedAt" time_format:"simple_datetime" time_location:"shanghai"`
Detail *UserDetail `bun:"rel:belongs-to,join:user_id=id" json:"-"`
func (UserCharge) TableName() string {
return "user_charge"
} }
type ChargeWithName struct { type ChargeWithName struct {
UserDetail `xorm:"extends"` UserDetail `bun:"extend"`
UserCharge `xorm:"extends"` UserCharge `bun:"extend"`
} }
func (ChargeWithName) TableName() string { var _ bun.BeforeAppendModelHook = (*UserCharge)(nil)
return "user_detail"
func (uc *UserCharge) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
uc.CreatedAt = oprTime
}
return nil
} }

View File

@@ -1,60 +1,69 @@
package model package model
import ( import (
"context"
"time" "time"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/uptrace/bun"
) )
type UserDetail struct { type UserDetail struct {
CreatedAndModifiedWithUser `xorm:"extends"` bun.BaseModel `bun:"table:user_detail,alias:d"`
DeletedWithUser `xorm:"extends"` CreatedAndModifiedWithUser `bun:"extend"`
Id string `xorm:"varchar(120) pk not null" json:"-"` DeletedWithUser `bun:"extend"`
Name *string `xorm:"varchar(100)" json:"name"` Id string `bun:",pk,notnull" json:"-"`
Abbr *string `xorm:"varchar(50)" json:"abbr"` Name *string `json:"name"`
Region *string `xorm:"varchar(10)" json:"region"` Abbr *string `json:"abbr"`
Address *string `xorm:"varchar(120)" json:"address"` Region *string `json:"region"`
Contact *string `xorm:"varchar(100)" json:"contact"` Address *string `json:"address"`
Phone *string `xorm:"varchar(50)" json:"phone"` Contact *string `json:"contact"`
UnitServiceFee decimal.Decimal `xorm:"numeric(8,2) not null" json:"unitServiceFee"` Phone *string `json:"phone"`
ServiceExpiration time.Time `xorm:"date not null" json:"serviceExpiration" time_format:"simple_date" time_location:"shanghai"` UnitServiceFee decimal.Decimal `bun:"type:numeric,notnull" json:"unitServiceFee"`
} ServiceExpiration Date `bun:"type:date,notnull" json:"serviceExpiration"`
func (UserDetail) TableName() string {
return "user_detail"
} }
type JoinedUserDetail struct { type JoinedUserDetail struct {
UserDetail `xorm:"extends"` UserDetail `bun:"extend"`
Id string `json:"id"` Id string `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Type int8 `json:"type"` Type int8 `json:"type"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
} }
func (JoinedUserDetail) TableName() string {
return "user"
}
type FullJoinedUserDetail struct { type FullJoinedUserDetail struct {
UserDetail `xorm:"extends"` UserDetail `bun:"extend"`
User `xorm:"extends"` User `bun:"extend"`
}
func (FullJoinedUserDetail) TableName() string {
return "user_detail"
} }
type UserDetailSimplified struct { type UserDetailSimplified struct {
Id string `xorm:"varchar(120) pk not null" json:"id"` bun.BaseModel `bun:"table:user_detail,alias:d"`
Name *string `xorm:"varchar(100)" json:"name"` Id string `bun:",pk,notnull" json:"id"`
Abbr *string `xorm:"varchar(50)" json:"abbr"` Name *string `json:"name"`
Region *string `xorm:"varchar(10)" json:"region"` Abbr *string `json:"abbr"`
Address *string `xorm:"varchar(120)" json:"address"` Region *string `json:"region"`
Contact *string `xorm:"varchar(100)" json:"contact"` Address *string `json:"address"`
Phone *string `xorm:"varchar(50)" json:"phone"` Contact *string `json:"contact"`
Phone *string `json:"phone"`
} }
func (UserDetailSimplified) TableName() string { func FromUserDetail(user UserDetail) UserDetailSimplified {
return "user_detail" dest := UserDetailSimplified{}
copier.Copy(&dest, user)
return dest
}
var _ bun.BeforeAppendModelHook = (*UserDetail)(nil)
func (d *UserDetail) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
d.CreatedAt = oprTime
d.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
d.LastModifiedAt = &oprTime
}
return nil
} }

View File

@@ -1,17 +1,35 @@
package model package model
import "github.com/shopspring/decimal" import (
"context"
"time"
"github.com/shopspring/decimal"
"github.com/uptrace/bun"
)
type WillDilutedFee struct { type WillDilutedFee struct {
CreatedAndModified `xorm:"extends"` bun.BaseModel `bun:"table:will_diluted_fee,alias:w"`
Id string `xorm:"varchar(120) pk not null" json:"id"` CreatedAndModified `bun:"extend"`
ReportId string `xorm:"varchar(120) not null" json:"reportId"` Id string `bun:",pk,notnull" json:"diluteId"`
SourceId *string `xorm:"varchar(120)" json:"sourceId"` ReportId string `bun:",notnull" json:"reportId"`
Name string `xorm:"varchar(50) not null" json:"name"` SourceId *string `json:"sourceId"`
Fee decimal.Decimal `xorm:"numeric(8,2) not null default 0" json:"fee"` Name string `bun:",notnull" json:"name"`
Memo *string `xorm:"text" json:"memo"` Fee decimal.Decimal `bun:"type:numeric,notnull" json:"fee"`
Memo *string `bun:"type:text" json:"memo"`
Origin *MaintenanceFee `bun:"rel:belongs-to,join:source_id=id"`
} }
func (WillDilutedFee) TableName() string { var _ bun.BeforeAppendModelHook = (*WillDilutedFee)(nil)
return "will_diluted_fee"
func (d *WillDilutedFee) BeforeAppendModel(ctx context.Context, query bun.Query) error {
oprTime := time.Now()
switch query.(type) {
case *bun.InsertQuery:
d.CreatedAt = oprTime
d.LastModifiedAt = &oprTime
case *bun.UpdateQuery:
d.LastModifiedAt = &oprTime
}
return nil
} }

View File

@@ -2,13 +2,12 @@ package response
import ( import (
"electricity_bill_calc/config" "electricity_bill_calc/config"
"net/http"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
type Result struct { type Result struct {
Ctx *gin.Context Ctx *fiber.Ctx
} }
type BaseResponse struct { type BaseResponse struct {
@@ -22,11 +21,11 @@ type PagedResponse struct {
Total int64 `json:"total"` Total int64 `json:"total"`
} }
func NewResult(ctx *gin.Context) *Result { func NewResult(ctx *fiber.Ctx) Result {
return &Result{Ctx: ctx} 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) var finalPayload = make(map[string]interface{}, 0)
finalPayload["code"] = code finalPayload["code"] = code
finalPayload["message"] = msg 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) { func (r Result) Failure(code int, msg string) error {
r.response(http.StatusInternalServerError, code, msg) return r.response(fiber.StatusInternalServerError, code, msg)
} }
// 操作出错(未成功)响应 // 操作出错(未成功)响应
func (r *Result) Error(code int, msg string) { func (r Result) Error(code int, msg string) error {
r.response(http.StatusOK, code, msg) 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) { func (r *Result) Unauthorized(msg string) error {
r.response(http.StatusOK, http.StatusUnauthorized, msg) return r.response(fiber.StatusOK, fiber.StatusUnauthorized, msg)
} }
// 简易操作成功信息 // 简易操作成功信息
func (r *Result) Success(msg string, payloads ...map[string]interface{}) { func (r *Result) Success(msg string, payloads ...map[string]interface{}) error {
r.response(http.StatusOK, http.StatusOK, msg, payloads...) return r.response(fiber.StatusOK, fiber.StatusOK, msg, payloads...)
} }
// 数据成功创建 // 数据成功创建
func (r *Result) Created(msg string, payloads ...map[string]interface{}) { func (r Result) Created(msg string, payloads ...map[string]interface{}) error {
r.response(http.StatusOK, http.StatusCreated, msg, payloads...) return r.response(fiber.StatusOK, fiber.StatusCreated, msg, payloads...)
} }
// 数据成功更新 // 数据成功更新
func (r *Result) Updated(msg string) { func (r Result) Updated(msg string) error {
r.response(http.StatusOK, http.StatusAccepted, msg) return r.response(fiber.StatusOK, fiber.StatusAccepted, msg)
} }
// 数据已成功删除 // 数据已成功删除
func (r *Result) Deleted(msg string) { func (r Result) Deleted(msg string) error {
r.response(http.StatusOK, http.StatusNoContent, msg) return r.response(fiber.StatusOK, fiber.StatusNoContent, msg)
} }
// 指定操作未被接受 // 指定操作未被接受
func (r *Result) BadRequest(msg string) { func (r Result) BadRequest(msg string) error {
r.response(http.StatusOK, http.StatusBadRequest, msg) return r.response(fiber.StatusOK, fiber.StatusBadRequest, msg)
} }
// 指定操作未被接受 // 指定操作未被接受
func (r *Result) NotAccept(msg string) { func (r Result) NotAccept(msg string) error {
r.response(http.StatusOK, http.StatusNotAcceptable, msg) return r.response(fiber.StatusOK, fiber.StatusNotAcceptable, msg)
} }
// 数据未找到 // 数据未找到
func (r *Result) NotFound(msg string) { func (r Result) NotFound(msg string) error {
r.response(http.StatusOK, http.StatusNotFound, msg) return r.response(fiber.StatusOK, fiber.StatusNotFound, msg)
} }
// 数据存在冲突 // 数据存在冲突
func (r *Result) Conflict(msg string) { func (r Result) Conflict(msg string) error {
r.response(http.StatusOK, http.StatusConflict, msg) return r.response(fiber.StatusOK, fiber.StatusConflict, msg)
} }
// 快速自由JSON格式响应 // 快速自由JSON格式响应
// ! 注意给定的map中同名的键会被覆盖。 // ! 注意给定的map中同名的键会被覆盖。
func (r *Result) Json(code int, msg string, payloads ...map[string]interface{}) { func (r Result) Json(code int, msg string, payloads ...map[string]interface{}) error {
r.response(http.StatusOK, code, msg, payloads...) return r.response(fiber.StatusOK, code, msg, payloads...)
} }
func NewPagedResponse(page int, total int64) *PagedResponse { 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{} { func (r PagedResponse) ToMap() map[string]interface{} {
return gin.H{ return fiber.Map{
"current": r.Page, "current": r.Page,
"pageSize": r.Size, "pageSize": r.Size,
"total": r.Total, "total": r.Total,

View File

@@ -3,6 +3,8 @@ package response
import ( import (
"electricity_bill_calc/model" "electricity_bill_calc/model"
"net/http" "net/http"
"github.com/gofiber/fiber/v2"
) )
type LoginResponse struct { type LoginResponse struct {
@@ -11,19 +13,19 @@ type LoginResponse struct {
Session *model.Session `json:"session"` Session *model.Session `json:"session"`
} }
func (r *Result) LoginSuccess(session *model.Session) { func (r Result) LoginSuccess(session *model.Session) error {
res := &LoginResponse{} res := &LoginResponse{}
res.Code = http.StatusOK res.Code = http.StatusOK
res.Message = "用户已成功登录。" res.Message = "用户已成功登录。"
res.NeedReset = false res.NeedReset = false
res.Session = session 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 := &LoginResponse{}
res.Code = http.StatusUnauthorized res.Code = http.StatusUnauthorized
res.Message = "用户凭据已失效。" res.Message = "用户凭据已失效。"
res.NeedReset = true res.NeedReset = true
r.Ctx.JSON(http.StatusOK, res) return r.Ctx.Status(fiber.StatusOK).JSON(res)
} }

View File

@@ -2,48 +2,79 @@ package router
import ( import (
"electricity_bill_calc/controller" "electricity_bill_calc/controller"
"electricity_bill_calc/response" "electricity_bill_calc/logger"
"electricity_bill_calc/security" "electricity_bill_calc/security"
"log" "fmt"
"runtime/debug" "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 { var json = jsontime.ConfigWithCustomTimeFormat
router := gin.Default()
router.Use(Recover)
router.Use(security.SessionRecovery)
controller.InitializeUserController(router) func init() {
controller.InitializeRegionController(router) timeZoneShanghai, _ := time.LoadLocation("Asia/Shanghai")
controller.InitializeChargesController(router) jsontime.AddTimeFormatAlias("simple_datetime", "2006-01-02 15:04:05")
controller.InitializeParkController(router) jsontime.AddTimeFormatAlias("simple_date", "2006-01-02")
controller.InitializeMaintenanceFeeController(router) jsontime.AddLocaleAlias("shanghai", timeZoneShanghai)
controller.InitializeMeter04kVController(router)
controller.InitializeReportController(router)
controller.InitializeEndUserController(router)
controller.InitializeWithdrawController(router)
controller.InitializeStatisticsController(router)
return router
} }
// 404 func App() *fiber.App {
func HandleNotFound(c *gin.Context) { app := fiber.New(fiber.Config{
response.NewResult(c).NotFound("指定资源未找到。") BodyLimit: 10 * 1024 * 1024,
EnablePrintRoutes: true,
EnableTrustedProxyCheck: false,
Prefork: false,
ErrorHandler: errorHandler,
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})
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.InitializeUserController(app)
controller.InitializeRegionController(app)
controller.InitializeChargesController(app)
controller.InitializeParkController(app)
controller.InitializeMaintenanceFeeController(app)
controller.InitializeMeter04kVController(app)
controller.InitializeReportController(app)
controller.InitializeEndUserController(app)
controller.InitializeWithdrawController(app)
controller.InitializeStatisticsController(app)
controller.InitializeGodModeController(app)
return app
} }
// 500 // 全局错误处理
func Recover(c *gin.Context) { func errorHandler(c *fiber.Ctx, err error) error {
defer func() { code := fiber.StatusInternalServerError
if r := recover(); r != nil { if e, ok := err.(*fiber.Error); ok {
//打印错误堆栈信息 code = e.Code
log.Printf("panic: %v\n", r)
debug.PrintStack()
// response.NewResult(c).Error(500, "服务器内部错误")
} }
}() e := c.Status(code).SendString(err.Error())
//继续后续接口调用 if e != nil {
c.Next() 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))
} }

View File

@@ -3,76 +3,69 @@ package security
import ( import (
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"net/http" "electricity_bill_calc/response"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gofiber/fiber/v2"
) )
// 用于解析Authorization头并从缓存中获取用户会话信息注入上下文的中间件。 // 用于解析Authorization头并从缓存中获取用户会话信息注入上下文的中间件。
// 如果没有获取到用户会话信息,将直接跳过会话信息注入。 // 如果没有获取到用户会话信息,将直接跳过会话信息注入。
// ! 仅通过该中间件是不能保证上下文中一定保存有用户会话信息的。 // ! 仅通过该中间件是不能保证上下文中一定保存有用户会话信息的。
func SessionRecovery(c *gin.Context) { func SessionRecovery(c *fiber.Ctx) error {
auth := c.Request.Header.Get("Authorization") if auth := c.Get("Authorization", ""); len(auth) > 0 {
if len(auth) > 0 {
token := strings.Fields(auth)[1] token := strings.Fields(auth)[1]
session, err := cache.RetreiveSession(token) session, err := cache.RetreiveSession(token)
if err == nil && session != nil { if err == nil && session != nil {
c.Set("session", session) c.Locals("session", session)
} }
} }
c.Next() return c.Next()
} }
// 用于强制确定用户已经登录了系统,即具有有效的用户会话 // 用于强制确定用户已经登录了系统,即具有有效的用户会话
// ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。 // ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。
func MustAuthenticated(c *gin.Context) { func MustAuthenticated(c *fiber.Ctx) error {
session, exists := c.Get("session") if session, ok := c.Locals("session").(*model.Session); !ok || session == nil {
if !exists || session == nil { return response.NewResult(c).Forbidden("用户必须在登录以后才能访问系统。")
c.AbortWithStatus(http.StatusForbidden)
} }
if _, ok := session.(*model.Session); !ok { return c.Next()
c.AbortWithStatus(http.StatusForbidden)
}
c.Next()
} }
// 用于对用户会话进行是否企业用户的判断 // 用于对用户会话进行是否企业用户的判断
// ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。 // ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。
func EnterpriseAuthorize(c *gin.Context) { func EnterpriseAuthorize(c *fiber.Ctx) error {
session, exists := c.Get("session") if sess, ok := c.Locals("session").(*model.Session); !ok || sess.Type != model.USER_TYPE_ENT {
if !exists || session == nil { return response.NewResult(c).Forbidden("指定接口资源只限企业用户访问。")
c.AbortWithStatus(http.StatusForbidden)
} }
if sess, ok := session.(*model.Session); !ok || sess.Type != model.USER_TYPE_ENT { return c.Next()
c.AbortWithStatus(http.StatusForbidden)
}
c.Next()
} }
// 用于对用户会话进行是否监管用户或运维用户的判断 // 用于对用户会话进行是否监管用户或运维用户的判断
// ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。 // ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。
func ManagementAuthorize(c *gin.Context) { func ManagementAuthorize(c *fiber.Ctx) error {
session, exists := c.Get("session") if sess, ok := c.Locals("session").(*model.Session); !ok || (sess.Type != model.USER_TYPE_SUP && sess.Type != model.USER_TYPE_OPS) {
if !exists || session == nil { r := response.NewResult(c)
c.AbortWithStatus(http.StatusForbidden) return r.Forbidden("指定接口资源只限市场监管用户访问。")
} }
if sess, ok := session.(*model.Session); !ok || (sess.Type != model.USER_TYPE_SUP && sess.Type != model.USER_TYPE_OPS) { return c.Next()
c.AbortWithStatus(http.StatusForbidden)
}
c.Next()
} }
// 用于对用户会话进行是否运维用户的判断 // 用于对用户会话进行是否运维用户的判断
// ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。 // ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。
func OPSAuthorize(c *gin.Context) { func OPSAuthorize(c *fiber.Ctx) error {
session, exists := c.Get("session") if sess, ok := c.Locals("session").(*model.Session); !ok || sess.Type != model.USER_TYPE_OPS {
if !exists { return response.NewResult(c).Forbidden("指定接口资源只限系统运维用户访问。")
c.AbortWithStatus(http.StatusForbidden)
} }
if sess, ok := session.(*model.Session); !ok || sess.Type != model.USER_TYPE_OPS { return c.Next()
c.AbortWithStatus(http.StatusForbidden) }
}
c.Next() // 用于对用户会话进行是否核心运维用户的判断
// ! 通过该中间件以后,是可以保证上下文中一定具有用户会话信息的。
func SingularityAuthorize(c *fiber.Ctx) error {
if sess, ok := c.Locals("session").(*model.Session); !ok || sess.Type != model.USER_TYPE_OPS || sess.Uid != "000" {
return response.NewResult(c).Forbidden("指定接口资源只限系统核心运维用户访问。")
}
return c.Next()
} }

View File

@@ -1,12 +0,0 @@
package service
func _postProcessSingle[T interface{}](instance *T, has bool, err error) (*T, error) {
if err != nil {
return nil, err
}
if has {
return instance, nil
} else {
return nil, nil
}
}

View File

@@ -1,6 +1,7 @@
package service package service
import ( import (
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
@@ -8,8 +9,6 @@ import (
"fmt" "fmt"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"xorm.io/builder"
"xorm.io/xorm/schemas"
) )
type _CalculateService struct{} type _CalculateService struct{}
@@ -17,221 +16,236 @@ type _CalculateService struct{}
var CalculateService _CalculateService var CalculateService _CalculateService
func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err error) { func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err error) {
ctx, cancel := global.TimeoutContext(12)
defer cancel()
// 资料准备 // 资料准备
var reportIndex = new(model.Report) var report = new(model.Report)
has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(reportIndex) err = global.DB.NewSelect().Model(report).
if err != nil || !has { Relation("Summary").
Relation("WillDilutedFees").
Relation("EndUsers").
Where("r.id = ?", reportId).
Scan(ctx)
if err != nil || report == nil {
return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err)
} }
var summary = new(model.ReportSummary)
has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary)
if err != nil || !has {
return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表概览", err)
}
var maintenanceFeeRecords = make([]model.WillDilutedFee, 0)
err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&maintenanceFeeRecords)
if err != nil {
return exceptions.NewNotFoundErrorFromError("未能获取到公示报表对应的待摊薄费用信息", err)
}
var endUserDetails = make([]*model.EndUserDetail, 0)
err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&endUserDetails)
if err != nil {
return exceptions.NewNotFoundErrorFromError("未获取到公示报表对应的终端用户抄表信息", err)
}
// 综合计算 // 综合计算
summary.CalculatePrices() report.Summary.CalculatePrices()
// 计算维护费总计 // 计算维护费总计
maintenanceFeeTotal := decimal.NewFromInt(0) maintenanceFeeTotal := decimal.NewFromInt(0)
for _, m := range maintenanceFeeRecords { for _, m := range report.WillDilutedFees {
maintenanceFeeTotal = maintenanceFeeTotal.Add(m.Fee) maintenanceFeeTotal = maintenanceFeeTotal.Add(m.Fee)
} }
// 计算终端用户信息与概览中的合计 // 计算终端用户信息与概览中的合计
summary.PublicConsumption = decimal.NewNullDecimal(decimal.Zero) report.Summary.Customers = model.NewConsumptions()
summary.PublicConsumptionCritical = decimal.NewNullDecimal(decimal.Zero) report.Summary.Publics = model.NewConsumptions()
summary.PublicConsumptionPeak = decimal.NewNullDecimal(decimal.Zero) for _, eu := range report.EndUsers {
summary.PublicConsumptionFlat = decimal.NewNullDecimal(decimal.Zero)
summary.PublicConsumptionValley = decimal.NewNullDecimal(decimal.Zero)
summary.CustomerConsumption = decimal.NewNullDecimal(decimal.Zero)
summary.CustomerConsumptionCritical = decimal.NewNullDecimal(decimal.Zero)
summary.CustomerConsumptionPeak = decimal.NewNullDecimal(decimal.Zero)
summary.CustomerConsumptionFlat = decimal.NewNullDecimal(decimal.Zero)
summary.CustomerConsumptionValley = decimal.NewNullDecimal(decimal.Zero)
for _, eu := range endUserDetails {
eu.OverallFee = decimal.NewNullDecimal( eu.OverallFee = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), eu.Overall.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
eu.CriticalFee = decimal.NewNullDecimal( eu.CriticalFee = decimal.NewNullDecimal(
eu.Critical.Decimal.Mul(summary.CriticalPrice.Decimal).RoundBank(2), eu.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2),
) )
eu.PeakFee = decimal.NewNullDecimal( eu.PeakFee = decimal.NewNullDecimal(
eu.Peak.Decimal.Mul(summary.PeakPrice.Decimal).RoundBank(2), eu.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2),
) )
eu.FlatFee = decimal.NewNullDecimal( eu.FlatFee = decimal.NewNullDecimal(
eu.Flat.Decimal.Mul(summary.FlatPrice.Decimal).RoundBank(2), eu.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2),
) )
eu.ValleyFee = decimal.NewNullDecimal( eu.ValleyFee = decimal.NewNullDecimal(
eu.Valley.Decimal.Mul(summary.ValleyPrice.Decimal).RoundBank(2), eu.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2),
) )
if eu.IsPublicMeter && eu.WillDilute { if eu.IsPublicMeter {
summary.PublicConsumption.Decimal = summary.PublicConsumption.Decimal.Add(eu.Overall.Decimal) report.Summary.Publics.Consumption.Decimal = report.Summary.Publics.Consumption.Decimal.Add(eu.Overall.Decimal)
summary.PublicConsumptionCritical.Decimal = summary.PublicConsumptionCritical.Decimal.Add(eu.Critical.Decimal) report.Summary.Publics.Critical.Decimal = report.Summary.Publics.Critical.Decimal.Add(eu.Critical.Decimal)
summary.PublicConsumptionPeak.Decimal = summary.PublicConsumptionPeak.Decimal.Add(eu.Peak.Decimal) report.Summary.Publics.Peak.Decimal = report.Summary.Publics.Peak.Decimal.Add(eu.Peak.Decimal)
summary.PublicConsumptionFlat.Decimal = summary.PublicConsumptionPeak.Decimal.Add(eu.Flat.Decimal) report.Summary.Publics.Flat.Decimal = report.Summary.Publics.Flat.Decimal.Add(eu.Flat.Decimal)
summary.PublicConsumptionValley.Decimal = summary.PublicConsumptionValley.Decimal.Add(eu.Valley.Decimal) report.Summary.Publics.Valley.Decimal = report.Summary.Publics.Valley.Decimal.Add(eu.Valley.Decimal)
} else { } else {
summary.CustomerConsumption.Decimal = summary.CustomerConsumption.Decimal.Add(eu.Overall.Decimal) report.Summary.Customers.Consumption.Decimal = report.Summary.Customers.Consumption.Decimal.Add(eu.Overall.Decimal)
summary.CustomerConsumptionCritical.Decimal = summary.CustomerConsumptionCritical.Decimal.Add(eu.Critical.Decimal) report.Summary.Customers.Critical.Decimal = report.Summary.Customers.Critical.Decimal.Add(eu.Critical.Decimal)
summary.CustomerConsumptionPeak.Decimal = summary.CustomerConsumptionPeak.Decimal.Add(eu.Peak.Decimal) report.Summary.Customers.Peak.Decimal = report.Summary.Customers.Peak.Decimal.Add(eu.Peak.Decimal)
summary.CustomerConsumptionFlat.Decimal = summary.CustomerConsumptionPeak.Decimal.Add(eu.Flat.Decimal) report.Summary.Customers.Flat.Decimal = report.Summary.Customers.Flat.Decimal.Add(eu.Flat.Decimal)
summary.CustomerConsumptionValley.Decimal = summary.CustomerConsumptionValley.Decimal.Add(eu.Valley.Decimal) report.Summary.Customers.Valley.Decimal = report.Summary.Customers.Valley.Decimal.Add(eu.Valley.Decimal)
} }
} }
// 计算户表总电费和公共总电费以及相应的摊薄 // 计算户表总电费和公共总电费以及相应的摊薄
summary.CustomerConsumptionFee = decimal.NewNullDecimal( if report.SubmeterType == model.CUSTOMER_METER_NON_PV {
summary.CustomerConsumption.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), // 计算终端用户部分
report.Summary.Customers.ConsumptionFee = decimal.NewNullDecimal(
report.Summary.Customers.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.CustomerConsumptionCriticalFee = decimal.NewNullDecimal( report.Summary.Customers.CriticalFee = decimal.NewNullDecimal(
summary.CustomerConsumptionCritical.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Customers.Critical.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.CustomerConsumptionPeakFee = decimal.NewNullDecimal( report.Summary.Customers.PeakFee = decimal.NewNullDecimal(
summary.CustomerConsumptionPeak.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Customers.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.CustomerConsumptionFlatFee = decimal.NewNullDecimal( report.Summary.Customers.FlatFee = decimal.NewNullDecimal(
summary.CustomerConsumptionFlat.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Customers.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.CustomerConsumptionValleyFee = decimal.NewNullDecimal( report.Summary.Customers.ValleyFee = decimal.NewNullDecimal(
summary.CustomerConsumptionValley.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Customers.Valley.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.PublicConsumptionFee = decimal.NewNullDecimal( // 计算公共区域部分
summary.PublicConsumption.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Publics.ConsumptionFee = decimal.NewNullDecimal(
report.Summary.Publics.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.PublicConsumptionCriticalFee = decimal.NewNullDecimal( report.Summary.Publics.CriticalFee = decimal.NewNullDecimal(
summary.PublicConsumptionCritical.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Publics.Critical.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.PublicConsumptionPeakFee = decimal.NewNullDecimal( report.Summary.Publics.PeakFee = decimal.NewNullDecimal(
summary.PublicConsumptionPeak.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Publics.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.PublicConsumptionFlatFee = decimal.NewNullDecimal( report.Summary.Publics.FlatFee = decimal.NewNullDecimal(
summary.PublicConsumptionFlat.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Publics.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
summary.PublicConsumptionValleyFee = decimal.NewNullDecimal( report.Summary.Publics.ValleyFee = decimal.NewNullDecimal(
summary.PublicConsumptionValley.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), report.Summary.Publics.Valley.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
) )
if summary.Overall.Abs().GreaterThan(decimal.Zero) { }
summary.PublicConsumptionProportion = decimal.NewNullDecimal( if report.SubmeterType == model.CUSTOMER_METER_PV {
summary.PublicConsumption.Decimal.Div(summary.Overall).RoundBank(15), // 计算终端用户部分
report.Summary.Customers.ConsumptionFee = decimal.NewNullDecimal(
report.Summary.Customers.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
)
report.Summary.Customers.CriticalFee = decimal.NewNullDecimal(
report.Summary.Customers.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2),
)
report.Summary.Customers.PeakFee = decimal.NewNullDecimal(
report.Summary.Customers.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2),
)
report.Summary.Customers.FlatFee = decimal.NewNullDecimal(
report.Summary.Customers.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2),
)
report.Summary.Customers.ValleyFee = decimal.NewNullDecimal(
report.Summary.Customers.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2),
)
// 计算公共区域部分
report.Summary.Publics.ConsumptionFee = decimal.NewNullDecimal(
report.Summary.Publics.Consumption.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2),
)
report.Summary.Publics.CriticalFee = decimal.NewNullDecimal(
report.Summary.Publics.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2),
)
report.Summary.Publics.PeakFee = decimal.NewNullDecimal(
report.Summary.Publics.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2),
)
report.Summary.Publics.FlatFee = decimal.NewNullDecimal(
report.Summary.Publics.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2),
)
report.Summary.Publics.ValleyFee = decimal.NewNullDecimal(
report.Summary.Publics.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2),
)
}
if report.Summary.Overall.Abs().GreaterThan(decimal.Zero) {
report.Summary.Customers.Proportion = decimal.NewNullDecimal(
report.Summary.Customers.Consumption.Decimal.Div(report.Summary.Overall).RoundBank(15),
)
report.Summary.Publics.Proportion = decimal.NewNullDecimal(
report.Summary.Publics.Consumption.Decimal.Div(report.Summary.Overall).RoundBank(15),
) )
} }
// 计算线损 // 计算线损
summary.Loss = decimal.NewNullDecimal( report.Summary.Loss = decimal.NewNullDecimal(
summary.Overall.Sub(summary.PublicConsumption.Decimal).Sub(summary.CustomerConsumption.Decimal), report.Summary.Overall.Sub(report.Summary.Publics.Consumption.Decimal).Sub(report.Summary.Customers.Consumption.Decimal),
) )
summary.LossFee = decimal.NewNullDecimal( report.Summary.LossFee = decimal.NewNullDecimal(
summary.Loss.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(8), report.Summary.Loss.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(8),
) )
if summary.Overall.Abs().GreaterThan(decimal.Zero) { if report.Summary.Overall.Abs().GreaterThan(decimal.Zero) {
summary.LossProportion = decimal.NewNullDecimal( report.Summary.LossProportion = decimal.NewNullDecimal(
summary.Loss.Decimal.Div(summary.Overall).RoundBank(15), report.Summary.Loss.Decimal.Div(report.Summary.Overall).RoundBank(15),
) )
if report.Summary.LossProportion.Decimal.GreaterThan(decimal.NewFromFloat(0.1)) {
report.Summary.AuthorizeLoss = decimal.NewNullDecimal(
report.Summary.Overall.Mul(decimal.NewFromFloat(0.1)).RoundBank(8),
)
report.Summary.AuthorizeLossFee = decimal.NewNullDecimal(
report.Summary.AuthorizeLoss.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(8),
)
} else {
report.Summary.AuthorizeLoss = report.Summary.Loss
report.Summary.AuthorizeLossFee = report.Summary.LossFee
} }
if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { }
summary.LossDilutedPrice = decimal.NewNullDecimal( if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) {
summary.LossFee.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(8), report.Summary.LossDilutedPrice = decimal.NewNullDecimal(
report.Summary.AuthorizeLossFee.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8),
) )
} }
// 计算基本电费和调整电费等的摊薄 // 计算基本电费和调整电费等的摊薄
if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) {
summary.BasicDilutedPrice = decimal.NewNullDecimal( report.Summary.BasicDilutedPrice = decimal.NewNullDecimal(
summary.BasicFee.Div(summary.CustomerConsumption.Decimal).RoundBank(8), report.Summary.BasicFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8),
) )
summary.AdjustDilutedPrice = decimal.NewNullDecimal( report.Summary.AdjustDilutedPrice = decimal.NewNullDecimal(
summary.AdjustFee.Div(summary.CustomerConsumption.Decimal).RoundBank(8), report.Summary.AdjustFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8),
) )
summary.MaintenanceDilutedPrice = decimal.NewNullDecimal( report.Summary.MaintenanceDilutedPrice = decimal.NewNullDecimal(
maintenanceFeeTotal.Div(summary.CustomerConsumption.Decimal).RoundBank(8), maintenanceFeeTotal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8),
)
summary.PublicConsumptionDilutedPrice = decimal.NewNullDecimal(
summary.PublicConsumptionFee.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(8),
) )
} }
// 计算摊薄总计 // 计算摊薄总计
summary.FinalDilutedOverall = decimal.NewNullDecimal( report.Summary.MaintenanceOverall = decimal.NewNullDecimal(maintenanceFeeTotal)
summary.BasicFee. report.Summary.FinalDilutedOverall = decimal.NewNullDecimal(
Add(summary.AdjustFee). report.Summary.BasicFee.
Add(summary.PublicConsumptionFee.Decimal). Add(report.Summary.AdjustFee).
Add(maintenanceFeeTotal). Add(report.Summary.AuthorizeLossFee.Decimal),
Add(summary.LossFee.Decimal),
) )
// 计算终端用户的全部摊薄内容 // 计算终端用户的全部摊薄内容
for _, eu := range endUserDetails { for _, eu := range report.EndUsers {
if eu.IsPublicMeter && eu.WillDilute {
// 计算需要摊薄的公共表计的摊薄内容
if summary.PublicConsumption.Decimal.Abs().GreaterThan(decimal.Zero) {
eu.OverallProportion = eu.Overall.Decimal.Div(summary.PublicConsumption.Decimal).RoundBank(15)
} else {
eu.OverallProportion = decimal.Zero
}
} else {
// 计算户表表计的摊薄内容 // 计算户表表计的摊薄内容
if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) {
eu.OverallProportion = eu.Overall.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(15) eu.OverallProportion = eu.Overall.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(15)
} else { } else {
eu.OverallProportion = decimal.Zero eu.OverallProportion = decimal.Zero
} }
eu.BasicFeeDiluted = decimal.NewNullDecimal( eu.BasicFeeDiluted = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.BasicDilutedPrice.Decimal).RoundBank(2), eu.Overall.Decimal.Mul(report.Summary.BasicDilutedPrice.Decimal).RoundBank(2),
) )
eu.AdjustFeeDiluted = decimal.NewNullDecimal( eu.AdjustFeeDiluted = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.AdjustDilutedPrice.Decimal).RoundBank(2), eu.Overall.Decimal.Mul(report.Summary.AdjustDilutedPrice.Decimal).RoundBank(2),
) )
eu.LossDiluted = decimal.NewNullDecimal( eu.LossDiluted = decimal.NewNullDecimal(
summary.Loss.Decimal.Mul(eu.OverallProportion).RoundBank(8), report.Summary.AuthorizeLoss.Decimal.Mul(eu.OverallProportion).RoundBank(8),
) )
eu.LossDiluted = decimal.NewNullDecimal( eu.LossFeeDiluted = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.LossDilutedPrice.Decimal).RoundBank(8), eu.Overall.Decimal.Mul(report.Summary.LossDilutedPrice.Decimal).RoundBank(8),
)
eu.MaintenanceFeeDiluted = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.MaintenanceDilutedPrice.Decimal).RoundBank(8),
)
eu.PublicConsumptionDiluted = decimal.NewNullDecimal(
eu.Overall.Decimal.Mul(summary.PublicConsumptionDilutedPrice.Decimal).RoundBank(8),
) )
eu.FinalDiluted = decimal.NewNullDecimal( eu.FinalDiluted = decimal.NewNullDecimal(
eu.BasicFeeDiluted.Decimal. eu.BasicFeeDiluted.Decimal.
Add(eu.AdjustFeeDiluted.Decimal). Add(eu.AdjustFeeDiluted.Decimal).
Add(eu.LossFeeDiluted.Decimal). Add(eu.LossFeeDiluted.Decimal).
Add(eu.MaintenanceFeeDiluted.Decimal).
Add(eu.PublicConsumptionDiluted.Decimal).
RoundBank(2), RoundBank(2),
) )
eu.FinalCharge = decimal.NewNullDecimal( eu.FinalCharge = decimal.NewNullDecimal(
eu.OverallFee.Decimal.Add(eu.FinalDiluted.Decimal).RoundBank(2), eu.OverallFee.Decimal.Add(eu.FinalDiluted.Decimal).RoundBank(2),
) )
} }
}
// 向数据库保存报表概况以及终端用户摊薄结果 // 向数据库保存报表概况以及终端用户摊薄结果
tx := global.DBConn.NewSession() tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err = tx.Begin(); err != nil { if err != nil {
return return
} }
defer tx.Close()
_, err = tx.ID(reportId).Update(summary) _, err = tx.NewUpdate().Model(report.Summary).WherePK().Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return return
} }
for _, eu := range endUserDetails { for _, eu := range report.EndUsers {
_, err = tx.ID(schemas.NewPK(eu.ReportId, eu.ParkId, eu.MeterId)).Update(eu) _, err = tx.NewUpdate().Model(eu).WherePK().Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return return
@@ -243,6 +257,6 @@ func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err er
tx.Rollback() tx.Rollback()
return return
} }
cache.AbolishRelation(fmt.Sprintf("publicity_%s", reportId)) cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId))
return return
} }

View File

@@ -1,10 +1,13 @@
package service package service
import ( import (
"context"
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
"strconv" "strconv"
@@ -12,28 +15,32 @@ import (
"github.com/fufuok/utils" "github.com/fufuok/utils"
"github.com/samber/lo" "github.com/samber/lo"
"xorm.io/builder" "github.com/uptrace/bun"
"xorm.io/xorm" "go.uber.org/zap"
) )
type _ChargeService struct{} type _ChargeService struct {
l *zap.Logger
}
var ChargeService _ChargeService var ChargeService = _ChargeService{
l: logger.Named("Service", "Charge"),
}
func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithIgnoreSettle bool) error { func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithIgnoreSettle bool) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
defer tx.Close() defer cancel()
if err := tx.Begin(); err != nil { tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
charge.Seq = 0 _, err = tx.NewInsert().Model(charge).Exec(ctx)
_, err := tx.Insert(charge)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
if extendWithIgnoreSettle { if extendWithIgnoreSettle {
err := c.updateUserExpiration(tx, charge.UserId, charge.ChargeTo) err := c.updateUserExpiration(&tx, ctx, charge.UserId, charge.ChargeTo)
if err != nil { if err != nil {
return err return err
} }
@@ -48,26 +55,35 @@ func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithI
} }
func (c _ChargeService) SettleCharge(seq int64, uid string) error { func (c _ChargeService) SettleCharge(seq int64, uid string) error {
var record *model.UserCharge ctx, cancel := global.TimeoutContext()
has, err := global.DBConn.Where(builder.Eq{"seq": seq, "user_id": uid}).NoAutoCondition().Get(record) defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err
}
var record = new(model.UserCharge)
err = tx.NewSelect().Model(&record).
Where("seq = ?", seq).
Where("user_id = ?", uid).
Scan(ctx)
if err != nil { if err != nil {
return nil return nil
} }
if !has { if record == nil {
return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。")
} }
tx := global.DBConn.NewSession()
defer tx.Close()
if err := tx.Begin(); err != nil {
return err
}
currentTime := time.Now() currentTime := time.Now()
_, err = tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("settled", "settled_at").Update(&model.UserCharge{Settled: true, SettledAt: &currentTime}) _, err = tx.NewUpdate().Model((*model.UserCharge)(nil)).
Where("seq = ?", seq).
Where("user_id = ?", uid).
Set("settled = ?", true).
Set("settled_at = ?", currentTime).
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
err = c.updateUserExpiration(tx, uid, record.ChargeTo) err = c.updateUserExpiration(&tx, ctx, uid, record.ChargeTo)
if err != nil { if err != nil {
return err return err
} }
@@ -76,33 +92,38 @@ func (c _ChargeService) SettleCharge(seq int64, uid string) error {
tx.Rollback() tx.Rollback()
return err return err
} }
cache.AbolishRelation("charge") cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq))
cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq))
return nil return nil
} }
func (c _ChargeService) RefundCharge(seq int64, uid string) error { func (c _ChargeService) RefundCharge(seq int64, uid string) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
defer tx.Close() defer cancel()
if err := tx.Begin(); err != nil { tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
currentTime := time.Now() currentTime := time.Now()
rows, err := tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("refunded", "refunded_at").Update(&model.UserCharge{Refunded: true, RefundedAt: &currentTime}) res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)).
Where("seq = ?", seq).
Where("user_id = ?", uid).
Set("refunded = ?", true).
Set("refunded_at = ?", currentTime).
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
tx.Rollback() tx.Rollback()
return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。")
} }
lastValidExpriation, err := c.lastValidChargeTo(uid) lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return exceptions.NewNotFoundError("未找到最后合法的计费时间。") return exceptions.NewNotFoundError("未找到最后合法的计费时间。")
} }
err = c.updateUserExpiration(tx, uid, lastValidExpriation) err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation)
if err != nil { if err != nil {
return err return err
} }
@@ -111,24 +132,29 @@ func (c _ChargeService) RefundCharge(seq int64, uid string) error {
tx.Rollback() tx.Rollback()
return err return err
} }
cache.AbolishRelation("charge") cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq))
cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq))
return nil return nil
} }
func (c _ChargeService) CancelCharge(seq int64, uid string) error { func (c _ChargeService) CancelCharge(seq int64, uid string) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
defer tx.Close() defer cancel()
if err := tx.Begin(); err != nil { tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
currentTime := time.Now() currentTime := time.Now()
rows, err := tx.Table(new(model.UserCharge)).Where(builder.Eq{"seq": seq, "user_id": uid}).Cols("cancelled", "cancelled_at").Update(&model.UserCharge{Cancelled: true, CancelledAt: &currentTime}) res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)).
Where("seq = ?", seq).
Where("user_id = ?", uid).
Set("cancelled = ?", true).
Set("cancelled_at = ?", currentTime).
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
tx.Rollback() tx.Rollback()
return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。")
} }
@@ -137,16 +163,15 @@ func (c _ChargeService) CancelCharge(seq int64, uid string) error {
tx.Rollback() tx.Rollback()
return err return err
} }
tx = global.DBConn.NewSession() tx, err = global.DB.BeginTx(ctx, &sql.TxOptions{})
defer tx.Close() if err != nil {
if err := tx.Begin(); err != nil {
return err return err
} }
lastValidExpriation, err := c.lastValidChargeTo(uid) lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid)
if err != nil { if err != nil {
return exceptions.NewNotFoundError("未找到最后合法的计费时间。") return exceptions.NewNotFoundError("未找到最后合法的计费时间。")
} }
err = c.updateUserExpiration(tx, uid, lastValidExpriation) err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation)
if err != nil { if err != nil {
return err return err
} }
@@ -156,30 +181,38 @@ func (c _ChargeService) CancelCharge(seq int64, uid string) error {
return err return err
} }
cache.AbolishRelation("user") cache.AbolishRelation("user")
cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) cache.AbolishRelation(fmt.Sprintf("user:%s", uid))
cache.AbolishRelation("charge") cache.AbolishRelation("charge")
cache.AbolishRelation(fmt.Sprintf("charge_%s_%d", uid, seq)) cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq))
return nil return nil
} }
func (_ChargeService) updateUserExpiration(tx *xorm.Session, uid string, expiration time.Time) error { func (ch _ChargeService) updateUserExpiration(tx *bun.Tx, ctx context.Context, uid string, expiration model.Date) error {
_, err := tx.ID(uid).Cols("service_expiration").Update(&model.UserDetail{ServiceExpiration: expiration}) _, err := tx.NewUpdate().Model((*model.UserDetail)(nil)).
Set("service_expiration = ?", expiration).
Where("id = ?", uid).
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
} }
cache.AbolishRelation("user") cache.AbolishRelation(fmt.Sprintf("user:%s", uid))
cache.AbolishRelation(fmt.Sprintf("user_%s", uid))
return err return err
} }
func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, page int) ([]model.ChargeWithName, int64, error) { func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, page int) ([]model.ChargeWithName, int64, error) {
var ( var (
cond = builder.NewCond() cond = global.DB.NewSelect()
condition = make([]string, 0) condition = make([]string, 0)
charges = make([]model.UserCharge, 0)
) )
cond = cond.Model(&charges).Relation("Detail")
condition = append(condition, strconv.Itoa(page)) condition = append(condition, strconv.Itoa(page))
if len(keyword) != 0 { if len(keyword) != 0 {
cond = cond.And(builder.Like{"d.name", keyword}.Or(builder.Like{"d.abbr", keyword})) keywordCond := "%" + keyword + "%"
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Where("detail.name like ?", keywordCond).
WhereOr("detail.abbr like ?", keywordCond)
})
condition = append(condition, keyword) condition = append(condition, keyword)
} }
if len(beginDate) != 0 { if len(beginDate) != 0 {
@@ -188,7 +221,7 @@ func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string,
if err != nil { if err != nil {
return make([]model.ChargeWithName, 0), 0, err return make([]model.ChargeWithName, 0), 0, err
} }
cond = cond.And(builder.Gte{"c.created_at": beginTime}) cond = cond.Where("c.created_at >= ?", beginTime)
condition = append(condition, strconv.FormatInt(beginTime.Unix(), 10)) condition = append(condition, strconv.FormatInt(beginTime.Unix(), 10))
} }
if len(endDate) != 0 { if len(endDate) != 0 {
@@ -197,64 +230,55 @@ func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string,
if err != nil { if err != nil {
return make([]model.ChargeWithName, 0), 0, err return make([]model.ChargeWithName, 0), 0, err
} }
cond = cond.And(builder.Lte{"c.created_at": endTime}) cond = cond.Where("c.created_at <= ?", endTime)
condition = append(condition, strconv.FormatInt(endTime.Unix(), 10)) condition = append(condition, strconv.FormatInt(endTime.Unix(), 10))
} }
if cachedTotal, err := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 && err == nil {
if cachedCharges, _ := cache.RetreiveSearch[[]model.ChargeWithName]("charge_with_name", condition...); cachedCharges != nil {
return *cachedCharges, cachedTotal, nil
}
}
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
var ( var (
total int64 total int
err error err error
) )
if cachedTotal, _ := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 { ctx, cancel := global.TimeoutContext()
total = cachedTotal defer cancel()
} else { total, err = cond.Limit(config.ServiceSettings.ItemsPageSize).Offset(startItem).ScanAndCount(ctx)
total, err = global.DBConn.
Alias("d"). relations := []string{"charge"}
Join("INNER", []string{"user_charge", "c"}, "c.user_id=d.id"). chargesWithName := make([]model.ChargeWithName, 0)
Where(cond). for _, c := range charges {
NoAutoCondition(). chargesWithName = append(chargesWithName, model.ChargeWithName{
Count(&model.ChargeWithName{}) UserCharge: c,
if err != nil { UserDetail: *c.Detail,
return nil, -1, err })
relations = append(relations, fmt.Sprintf("charge:%s:%d", c.UserId, c.Seq))
} }
cache.CacheCount([]string{"charge"}, "charge_with_name", total, condition...)
} cache.CacheCount(relations, "charge_with_name", int64(total), condition...)
charges := make([]model.ChargeWithName, 0) cache.CacheSearch(chargesWithName, relations, "charge_with_name", condition...)
if cachedCharges, _ := cache.RetreiveSearch[[]model.ChargeWithName]("charge_with_name", condition...); cachedCharges != nil { return chargesWithName, int64(total), err
return *cachedCharges, total, nil
}
err = global.DBConn.
Alias("d").
Join("INNER", []string{"user_charge", "c"}, "c.user_id=d.id").
Where(cond).
Limit(config.ServiceSettings.ItemsPageSize, startItem).
NoAutoCondition().
Find(&charges)
cache.CacheSearch(charges, []string{"charge"}, "charge_with_name", condition...)
return charges, total, err
} }
func (_ChargeService) lastValidChargeTo(uid string) (time.Time, error) { func (_ChargeService) lastValidChargeTo(tx *bun.Tx, ctx *context.Context, uid string) (model.Date, error) {
veryBlankTime, _ := time.Parse("2006-01-02 15:04:05", "0001-01-01 00:00:00") var records []model.Date
var records []string err := tx.NewSelect().Table("user_charge").
err := global.DBConn. Where("settled = ? and cancelled = ? and refunded = ? and user_id = ?", true, false, false, uid).
Table(&model.UserCharge{}). Column("charge_to").
Where(builder.Eq{"settled": true, "cancelled": false, "refunded": false, "user_id": uid}). Scan(*ctx, &records)
Cols("charge_to").
Find(&records)
if err != nil { if err != nil {
return veryBlankTime, nil return model.NewEmptyDate(), nil
} }
mappedRecords := lo.Map(records, func(elem string, index int) time.Time { lastValid := lo.Reduce(records, func(acc, elem model.Date, index int) model.Date {
t, _ := time.Parse(time.RFC3339, elem) if elem.Time.After(acc.Time) {
return utils.BeginOfDay(t)
})
lastValid := lo.Reduce(mappedRecords, func(acc, elem time.Time, index int) time.Time {
if elem.After(acc) {
return elem return elem
} else { } else {
return acc return acc
} }
}, veryBlankTime) }, model.NewEmptyDate())
return lastValid, nil return lastValid, nil
} }

View File

@@ -1,80 +1,99 @@
package service package service
import ( import (
"context"
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/excel" "electricity_bill_calc/excel"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"time"
mapset "github.com/deckarep/golang-set/v2"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"xorm.io/builder" "github.com/uptrace/bun"
"xorm.io/xorm" "go.uber.org/zap"
"xorm.io/xorm/schemas"
) )
type _EndUserService struct{} type _EndUserService struct {
l *zap.Logger
}
var EndUserService _EndUserService type MeterAppears struct {
Meter string
Appears int64
}
var EndUserService = _EndUserService{
l: logger.Named("Service", "EndUser"),
}
func (_EndUserService) SearchEndUserRecord(reportId, keyword string, page int) ([]model.EndUserDetail, int64, error) { func (_EndUserService) SearchEndUserRecord(reportId, keyword string, page int) ([]model.EndUserDetail, int64, error) {
var conditions = make([]string, 0) var (
conditions = append(conditions, reportId, strconv.Itoa(page)) conditions = make([]string, 0)
cond := builder.NewCond().And(builder.Eq{"report_id": reportId}) endUsers = make([]model.EndUserDetail, 0)
if len(keyword) > 0 { cond = global.DB.NewSelect().Model(&endUsers)
cond = cond.And(
builder.Like{"customer_name", keyword}.
Or(builder.Like{"contact_name", keyword}).
Or(builder.Like{"contact_phone", keyword}).
Or(builder.Like{"meter_04kv_id", keyword}),
) )
conditions = append(conditions, reportId, strconv.Itoa(page))
cond = cond.Where("report_id = ?", reportId)
if len(keyword) > 0 {
keywordCond := "%" + keyword + "%"
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Where("customer_name like ?", keywordCond).
WhereOr("contact_name like ?", keywordCond).
WhereOr("contact_phone like ?", keywordCond).
WhereOr("meter_04kv_id like ?", keywordCond)
})
conditions = append(conditions, keyword) conditions = append(conditions, keyword)
} }
var ( if cachedTotal, err := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 && err == nil {
total int64
err error
)
if cachedTotal, _ := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 {
total = cachedTotal
} else {
total, err = global.DBConn.
Table(&model.EndUserDetail{}).
Where(cond).
Count()
if err != nil {
return make([]model.EndUserDetail, 0), -1, err
}
cache.CacheCount([]string{"end_user", "report", "park"}, "end_user_detail", total, conditions...)
}
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", conditions...); cachedEndUsers != nil { if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", conditions...); cachedEndUsers != nil {
return *cachedEndUsers, total, nil return *cachedEndUsers, cachedTotal, nil
} }
endUsers := make([]model.EndUserDetail, 0) }
err = global.DBConn.
Where(cond). ctx, cancel := global.TimeoutContext()
Limit(config.ServiceSettings.ItemsPageSize, startItem). defer cancel()
Asc("seq").
Find(&endUsers) startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
cache.CacheSearch(endUsers, []string{"end_user", "report", "park"}, "end_user_detail", conditions...) total, err := cond.Limit(config.ServiceSettings.ItemsPageSize).
return endUsers, total, err Offset(startItem).
Order("seq asc", "meter_04kv_id asc").
ScanAndCount(ctx)
relations := []string{"end_user", "report", "park"}
for _, eu := range endUsers {
relations = append(relations, fmt.Sprintf("end_user:%s:%s", eu.ReportId, eu.MeterId))
}
cache.CacheCount(relations, "end_user_detail", int64(total), conditions...)
cache.CacheSearch(endUsers, relations, "end_user_detail", conditions...)
return endUsers, int64(total), err
} }
func (_EndUserService) AllEndUserRecord(reportId string) ([]model.EndUserDetail, error) { func (_EndUserService) AllEndUserRecord(reportId string) ([]model.EndUserDetail, error) {
if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", "report", reportId); cachedEndUsers != nil { if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", "report", reportId); cachedEndUsers != nil {
return *cachedEndUsers, nil return *cachedEndUsers, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
users := make([]model.EndUserDetail, 0) users := make([]model.EndUserDetail, 0)
err := global.DBConn. err := global.DB.NewSelect().Model(&users).
Where(builder.Eq{"report_id": reportId}). Where("report_id = ?", reportId).
Asc("seq"). Order("seq asc", "meter_04kv_id asc").
Find(&users) Scan(ctx)
cache.CacheSearch(users, []string{"end_user_detail", "report", "park"}, "end_user_detail", "report", reportId) relations := lo.Map(users, func(eu model.EndUserDetail, _ int) string {
return fmt.Sprintf("end_user:%s:%s", eu.ReportId, eu.MeterId)
})
relations = append(relations, "report", "park")
cache.CacheSearch(users, relations, "end_user_detail", "report", reportId)
return users, err return users, err
} }
@@ -82,25 +101,28 @@ func (_EndUserService) FetchSpecificEndUserRecord(reportId, parkId, meterId stri
if cachedEndUser, _ := cache.RetreiveEntity[model.EndUserDetail]("end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)); cachedEndUser != nil { if cachedEndUser, _ := cache.RetreiveEntity[model.EndUserDetail]("end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)); cachedEndUser != nil {
return cachedEndUser, nil return cachedEndUser, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
record := new(model.EndUserDetail) record := new(model.EndUserDetail)
_, err := global.DBConn. err := global.DB.NewSelect().Model(record).
ID(schemas.NewPK(reportId, parkId, meterId)). Where("report_id = ?", reportId).
NoAutoCondition(). Where("park_id = ?", parkId).
Get(record) Where("meter_04kv_id = ?", meterId).
cache.CacheEntity(record, []string{"end_user_detail", "report", "park"}, "end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)) Scan(ctx)
cache.CacheEntity(record, []string{fmt.Sprintf("end_user:%s:%s", reportId, meterId), "report", "park"}, "end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId))
return record, err return record, err
} }
func (_EndUserService) UpdateEndUserRegisterRecord(tx *xorm.Session, record model.EndUserDetail) (err error) { func (_EndUserService) UpdateEndUserRegisterRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail) (err error) {
record.CalculatePeriod() record.CalculatePeriod()
_, err = tx.ID(schemas.NewPK(record.ReportId, record.ParkId, record.MeterId)). updateColumns := []string{
Cols(
"current_period_overall", "current_period_overall",
"adjust_overall", "adjust_overall",
"current_period_critical", "current_period_critical",
"current_period_peak", "current_period_peak",
"current_period_flat", "current_period_flat",
"current_perios_valley", "current_period_valley",
"adjust_critical", "adjust_critical",
"adjust_peak", "adjust_peak",
"adjust_flat", "adjust_flat",
@@ -110,9 +132,22 @@ func (_EndUserService) UpdateEndUserRegisterRecord(tx *xorm.Session, record mode
"peak", "peak",
"flat", "flat",
"valley", "valley",
). }
Update(record) if record.Initialize {
cache.AbolishRelation("end_user_detail") updateColumns = append(updateColumns,
"last_period_overall",
"last_period_critical",
"last_period_peak",
"last_period_flat",
"last_period_valley",
)
}
_, err = tx.NewUpdate().Model(&record).
WherePK().
Column(updateColumns...).
Exec(*ctx)
cache.AbolishRelation(fmt.Sprintf("end_user:%s:%s", record.ReportId, record.MeterId))
return return
} }
@@ -121,12 +156,34 @@ func (_EndUserService) newVirtualExcelAnalysisError(err error) *excel.ExcelAnaly
} }
func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Reader) *exceptions.BatchError { func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Reader) *exceptions.BatchError {
ctx, cancel := global.TimeoutContext(120)
defer cancel()
errs := exceptions.NewBatchError() errs := exceptions.NewBatchError()
users, err := es.AllEndUserRecord(reportId) users, err := es.AllEndUserRecord(reportId)
if err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs return errs
} }
var reportDetail = new(model.Report)
err = global.DB.NewSelect().Model(reportDetail).
Where("id = ?", reportId).
Scan(ctx)
if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(fmt.Errorf("未能找到相应的报表。%w", err)))
return errs
}
meterAppers := make([]MeterAppears, 0)
err = global.DB.NewSelect().Model((*model.EndUserDetail)(nil)).
ColumnExpr("meter_04kv_id as meter").
ColumnExpr("count(*) as appears").
Where("park_id = ?", reportDetail.ParkId).
Group("meter_04kv_id").
Scan(ctx, &meterAppers)
if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs
}
indexedUsers := lo.Reduce( indexedUsers := lo.Reduce(
users, users,
func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail { func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail {
@@ -147,24 +204,37 @@ func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Read
} }
return errs return errs
} }
tx := global.DBConn.NewSession() tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err = tx.Begin(); err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs return errs
} }
defer tx.Close()
for _, im := range imports { for _, im := range imports {
if elem, ok := indexedUsers[im.MeterId]; ok { if elem, ok := indexedUsers[im.MeterId]; ok {
if appears, has := lo.Find(meterAppers, func(m MeterAppears) bool {
return m.Meter == elem.MeterId
}); has {
if appears.Appears <= 1 {
elem.LastPeriodOverall = im.LastPeriodOverall
elem.LastPeriodCritical = decimal.Zero
elem.LastPeriodPeak = decimal.Zero
elem.LastPeriodValley = decimal.Zero
elem.LastPeriodFlat = elem.LastPeriodOverall.Sub(elem.LastPeriodCritical).Sub(elem.LastPeriodPeak).Sub(elem.LastPeriodValley)
elem.Initialize = true
}
}
elem.CurrentPeriodOverall = im.CurrentPeriodOverall elem.CurrentPeriodOverall = im.CurrentPeriodOverall
elem.AdjustOverall = im.AdjustOverall elem.AdjustOverall = im.AdjustOverall
elem.CurrentPeriodCritical = decimal.Zero elem.CurrentPeriodCritical = decimal.Zero
elem.CurrentPeriodPeak = decimal.Zero elem.CurrentPeriodPeak = decimal.Zero
elem.CurrentPeriodValley = decimal.Zero elem.CurrentPeriodValley = decimal.Zero
elem.CurrentPeriodFlat = elem.CurrentPeriodOverall.Sub(elem.CurrentPeriodCritical).Sub(elem.CurrentPeriodPeak).Sub(elem.CurrentPeriodValley)
elem.AdjustCritical = decimal.Zero elem.AdjustCritical = decimal.Zero
elem.AdjustPeak = decimal.Zero elem.AdjustPeak = decimal.Zero
elem.AdjustValley = decimal.Zero elem.AdjustValley = decimal.Zero
err := es.UpdateEndUserRegisterRecord(tx, elem) elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley)
err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem)
if err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
} }
@@ -187,12 +257,32 @@ func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Read
} }
func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader) *exceptions.BatchError { func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader) *exceptions.BatchError {
ctx, cancel := global.TimeoutContext(120)
defer cancel()
errs := exceptions.NewBatchError() errs := exceptions.NewBatchError()
users, err := es.AllEndUserRecord(reportId) users, err := es.AllEndUserRecord(reportId)
if err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs return errs
} }
var reportDetail = new(model.Report)
err = global.DB.NewSelect().Model(reportDetail).Where("id = ?", reportId).Scan(ctx)
if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(fmt.Errorf("未能找到相应的报表。%w", err)))
return errs
}
meterAppers := make([]MeterAppears, 0)
err = global.DB.NewSelect().Model((*model.EndUserDetail)(nil)).
ColumnExpr("meter_04kv_id as meter").
ColumnExpr("count(*) as appears").
Where("park_id = ?", reportDetail.Id).
Group("meter_04kv_id").
Scan(ctx, &meterAppers)
if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs
}
indexedUsers := lo.Reduce( indexedUsers := lo.Reduce(
users, users,
func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail { func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail {
@@ -201,7 +291,7 @@ func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader)
}, },
make(map[string]model.EndUserDetail, 0), make(map[string]model.EndUserDetail, 0),
) )
analyzer, err := excel.NewEndUserNonPVExcelAnalyzer(file) analyzer, err := excel.NewEndUserPVExcelAnalyzer(file)
if err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs return errs
@@ -213,24 +303,38 @@ func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader)
} }
return errs return errs
} }
tx := global.DBConn.NewSession()
if err = tx.Begin(); err != nil { tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
return errs return errs
} }
defer tx.Close()
for _, im := range imports { for _, im := range imports {
if elem, ok := indexedUsers[im.MeterId]; ok { if elem, ok := indexedUsers[im.MeterId]; ok {
if appears, has := lo.Find(meterAppers, func(m MeterAppears) bool {
return m.Meter == elem.MeterId
}); has {
if appears.Appears <= 1 {
elem.LastPeriodOverall = im.LastPeriodOverall
elem.LastPeriodCritical = im.LastPeriodCritical.Decimal
elem.LastPeriodPeak = im.LastPeriodPeak.Decimal
elem.LastPeriodValley = im.LastPeriodValley.Decimal
elem.LastPeriodFlat = elem.LastPeriodOverall.Sub(elem.LastPeriodCritical).Sub(elem.LastPeriodPeak).Sub(elem.LastPeriodValley)
elem.Initialize = true
}
}
elem.CurrentPeriodOverall = im.CurrentPeriodOverall elem.CurrentPeriodOverall = im.CurrentPeriodOverall
elem.AdjustOverall = im.AdjustOverall elem.AdjustOverall = im.AdjustOverall
elem.CurrentPeriodCritical = im.CurrentPeriodCritical.Decimal elem.CurrentPeriodCritical = im.CurrentPeriodCritical.Decimal
elem.CurrentPeriodPeak = im.CurrentPeriodPeak.Decimal elem.CurrentPeriodPeak = im.CurrentPeriodPeak.Decimal
elem.CurrentPeriodValley = im.CurrentPeriodValley.Decimal elem.CurrentPeriodValley = im.CurrentPeriodValley.Decimal
elem.CurrentPeriodFlat = elem.CurrentPeriodOverall.Sub(elem.CurrentPeriodCritical).Sub(elem.CurrentPeriodPeak).Sub(elem.CurrentPeriodValley)
elem.AdjustCritical = im.AdjustCritical.Decimal elem.AdjustCritical = im.AdjustCritical.Decimal
elem.AdjustPeak = im.AdjustPeak.Decimal elem.AdjustPeak = im.AdjustPeak.Decimal
elem.AdjustValley = im.AdjustValley.Decimal elem.AdjustValley = im.AdjustValley.Decimal
err := es.UpdateEndUserRegisterRecord(tx, elem) elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley)
err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem)
if err != nil { if err != nil {
errs.AddError(es.newVirtualExcelAnalysisError(err)) errs.AddError(es.newVirtualExcelAnalysisError(err))
} }
@@ -251,3 +355,125 @@ func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader)
cache.AbolishRelation("end_user_detail") cache.AbolishRelation("end_user_detail")
return errs return errs
} }
func (es _EndUserService) StatEndUserRecordInPeriod(requestUser, requestPark, startDate, endDate string) ([]model.EndUserPeriodStat, error) {
var (
conditions = make([]string, 0)
relations = []string{
fmt.Sprintf("park:%s", requestPark),
"end_user_detail",
}
cond = global.DB.NewSelect().
Model((*model.EndUserDetail)(nil)).
Relation("Report", func(sq *bun.SelectQuery) *bun.SelectQuery {
return sq.ExcludeColumn("*")
}).
Relation("Park", func(sq *bun.SelectQuery) *bun.SelectQuery {
return sq.ExcludeColumn("*")
})
)
if len(requestUser) > 0 {
cond = cond.Where("park.user_id = ?", requestUser)
conditions = append(conditions, requestUser)
} else {
conditions = append(conditions, "_")
}
if len(requestPark) > 0 {
cond = cond.Where("eud.park_id = ?", requestPark)
conditions = append(conditions, requestPark)
} else {
conditions = append(conditions, "_")
}
if len(startDate) > 0 {
parseTime, err := time.Parse("2006-01", startDate)
if err != nil {
return make([]model.EndUserPeriodStat, 0), fmt.Errorf("不能解析给定的参数[startDate]%w", err)
}
start := model.NewDate(parseTime)
cond = cond.Where("report.period >= ?::date", start.ToString())
conditions = append(conditions, startDate)
} else {
conditions = append(conditions, "_")
}
if len(endDate) > 0 {
parseTime, err := time.Parse("2006-01", endDate)
if err != nil {
return make([]model.EndUserPeriodStat, 0), fmt.Errorf("不能解析给定的参数[endDate]%w", err)
}
end := model.NewDate(parseTime)
cond = cond.Where("report.period <= ?::date", end.ToString())
conditions = append(conditions, endDate)
}
if cached, err := cache.RetreiveSearch[[]model.EndUserPeriodStat]("end_user_stat", conditions...); cached != nil && err == nil {
return *cached, nil
}
ctx, cancel := global.TimeoutContext(120)
defer cancel()
var endUserSums []model.EndUserPeriodStat
err := cond.Column("eud.meter_04kv_id", "eud.park_id").
ColumnExpr("sum(?) as overall", bun.Ident("eud.overall")).
ColumnExpr("sum(?) as overall_fee", bun.Ident("eud.overall_fee")).
ColumnExpr("sum(?) as critical", bun.Ident("eud.critical")).
ColumnExpr("sum(?) as critical_fee", bun.Ident("eud.critical_fee")).
ColumnExpr("sum(?) as peak", bun.Ident("eud.peak")).
ColumnExpr("sum(?) as peak_fee", bun.Ident("eud.peak_fee")).
ColumnExpr("sum(?) as valley", bun.Ident("eud.valley")).
ColumnExpr("sum(?) as valley_fee", bun.Ident("eud.valley_fee")).
ColumnExpr("sum(?) as final_diluted", bun.Ident("eud.final_diluted")).
Where("report.published = ?", true).
Group("eud.meter_04kv_id", "eud.park_id").
Scan(ctx, &endUserSums)
if err != nil {
return make([]model.EndUserPeriodStat, 0), fmt.Errorf("未能完成终端用户在指定期限内的统计,%w", err)
}
parkIds := lo.Reduce(
endUserSums,
func(acc mapset.Set[string], elem model.EndUserPeriodStat, _ int) mapset.Set[string] {
acc.Add(elem.ParkId)
return acc
},
mapset.NewSet[string](),
)
meterArchives := make([]model.Meter04KV, 0)
if len(parkIds.ToSlice()) > 0 {
err = global.DB.NewSelect().
Model(&meterArchives).Relation("ParkDetail").
Where("park_id in (?)", bun.In(parkIds.ToSlice())).
Scan(ctx)
if err != nil {
return make([]model.EndUserPeriodStat, 0), fmt.Errorf("未能获取到终端表计的最新基础档案,%w", err)
}
}
filledStats := lo.Map(
endUserSums,
func(elem model.EndUserPeriodStat, _ int) model.EndUserPeriodStat {
archive, has := lo.Find(meterArchives, func(meter model.Meter04KV) bool {
return meter.Code == elem.MeterId
})
if has {
if archive.Address != nil {
elem.Address = *archive.Address
} else {
elem.Address = ""
}
if archive.CustomerName != nil {
elem.CustomerName = *archive.CustomerName
} else {
elem.CustomerName = ""
}
elem.IsPublicMeter = archive.IsPublicMeter
elem.Kind = archive.ParkDetail.SubmeterType
}
if elem.OverallFee.Valid && elem.AdjustFee.Valid && !elem.OverallFee.Decimal.IsZero() {
elem.AdjustProportion = decimal.NewNullDecimal(
elem.AdjustFee.Decimal.Div(elem.OverallFee.Decimal).RoundBank(8),
)
} else {
elem.AdjustProportion = decimal.NullDecimal{}
}
return elem
},
)
cache.CacheSearch(filledStats, relations, "end_user_stat", conditions...)
return filledStats, nil
}

875
service/god_mode.go Normal file
View File

@@ -0,0 +1,875 @@
package service
import (
"context"
"database/sql"
"electricity_bill_calc/cache"
"electricity_bill_calc/exceptions"
"electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model"
"fmt"
"time"
"github.com/samber/lo"
"github.com/shopspring/decimal"
"github.com/uptrace/bun"
"go.uber.org/zap"
)
type _GodModeService struct {
l *zap.Logger
}
var GodModeService = _GodModeService{
l: logger.Named("Service", "GodMode"),
}
// 从此处开始为删除报表相关的部分
func (_GodModeService) resetReportIndex(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
var report = new(model.Report)
err := tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx)
if err != nil {
tx.Rollback()
return false, exceptions.NewNotFoundError("指定报表索引未找到。")
}
report.StepState.Summary = false
report.StepState.WillDiluted = false
report.StepState.Submeter = false
report.StepState.Calculate = false
report.StepState.Preview = false
report.StepState.Publish = false
report.Published = false
report.PublishedAt = nil
report.Withdraw = model.REPORT_NOT_WITHDRAW
report.LastWithdrawAppliedAt = nil
report.LastWithdrawAuditAt = nil
res, err := tx.NewUpdate().Model(report).
WherePK().
Column(
"step_state",
"published",
"published_at",
"withdraw",
"last_withdraw_applied_at",
"last_withdraw_audit_at",
).
Exec(*ctx)
if affected, _ := res.RowsAffected(); err != nil || affected == 0 {
tx.Rollback()
return false, err
}
return true, err
}
func (_GodModeService) resetReportSummary(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
var summary = &model.ReportSummary{
ReportId: reportId,
}
_, err := tx.NewUpdate().Model(summary).WherePK().Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
var report = new(model.Report)
err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
report.StepState.Summary = false
res, err := tx.NewUpdate().Model(report).
Column("step_state").
WherePK().
Exec(*ctx)
rows, _ := res.RowsAffected()
if err != nil {
tx.Rollback()
}
return rows >= 0, err
}
func (_GodModeService) flushReportMaintenances(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
_, err := tx.NewDelete().Model((*model.WillDilutedFee)(nil)).
Where("report_id = ?", reportId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
var report = new(model.Report)
err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
report.StepState.WillDiluted = false
res, err := tx.NewUpdate().Model(report).
WherePK().
Column("step_state").
Exec(*ctx)
rows, _ := res.RowsAffected()
if err != nil {
tx.Rollback()
}
return rows >= 0, err
}
func (g _GodModeService) resetSingleEndUserRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail, additionalColumns ...string) (bool, error) {
record.CurrentPeriodOverall = decimal.Zero
record.CurrentPeriodCritical = decimal.Zero
record.CurrentPeriodPeak = decimal.Zero
record.CurrentPeriodFlat = decimal.Zero
record.CurrentPeriodValley = decimal.Zero
record.AdjustOverall = decimal.Zero
record.AdjustCritical = decimal.Zero
record.AdjustPeak = decimal.Zero
record.AdjustFlat = decimal.Zero
record.AdjustValley = decimal.Zero
record.Overall = decimal.NewNullDecimal(decimal.Zero)
record.Overall.Valid = false
record.OverallFee = decimal.NewNullDecimal(decimal.Zero)
record.OverallFee.Valid = false
record.OverallProportion = decimal.Zero
record.Critical = decimal.NewNullDecimal(decimal.Zero)
record.Critical.Valid = false
record.CriticalFee = decimal.NewNullDecimal(decimal.Zero)
record.CriticalFee.Valid = false
record.Peak = decimal.NewNullDecimal(decimal.Zero)
record.Peak.Valid = false
record.PeakFee = decimal.NewNullDecimal(decimal.Zero)
record.PeakFee.Valid = false
record.Flat = decimal.NewNullDecimal(decimal.Zero)
record.Flat.Valid = false
record.FlatFee = decimal.NewNullDecimal(decimal.Zero)
record.FlatFee.Valid = false
record.Valley = decimal.NewNullDecimal(decimal.Zero)
record.Valley.Valid = false
record.ValleyFee = decimal.NewNullDecimal(decimal.Zero)
record.ValleyFee.Valid = false
record.BasicFeeDiluted = decimal.NewNullDecimal(decimal.Zero)
record.BasicFeeDiluted.Valid = false
record.AdjustFeeDiluted = decimal.NewNullDecimal(decimal.Zero)
record.AdjustFeeDiluted.Valid = false
record.LossDiluted = decimal.NewNullDecimal(decimal.Zero)
record.LossDiluted.Valid = false
record.LossFeeDiluted = decimal.NewNullDecimal(decimal.Zero)
record.LossFeeDiluted.Valid = false
record.FinalDiluted = decimal.NewNullDecimal(decimal.Zero)
record.FinalDiluted.Valid = false
record.FinalCharge = decimal.NewNullDecimal(decimal.Zero)
record.FinalCharge.Valid = false
columns := []string{
"current_period_overall",
"current_period_critical",
"current_period_peak",
"current_period_flat",
"current_period_valley",
"adjust_overall",
"adjust_critical",
"adjust_peak",
"adjust_flat",
"adjust_valley",
"overall",
"overall_fee",
"overall_proportion",
"critical",
"critical_fee",
"peak",
"peak_fee",
"flat",
"flat_fee",
"valley",
"valley_fee",
"basic_fee_diluted",
"adjust_fee_diluted",
"loss_diluted",
"loss_fee_diluted",
"maintenance_fee_diluted",
"public_consumption_diluted",
"final_diluted",
"final_charge",
}
columns = append(columns, additionalColumns...)
_, err := tx.NewUpdate().Model(&record).
WherePK().
Column(columns...).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
return true, nil
}
func (g _GodModeService) resynchronizeEndUserArchives(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
var currentRecords = make([]*model.EndUserDetail, 0)
err := tx.NewSelect().Model(&currentRecords).
Where("report_id = ?", reportId).
Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
var report = new(model.Report)
err = tx.NewSelect().Model(report).
Where("id = ?", reportId).
Scan(*ctx)
if err != nil || report == nil {
tx.Rollback()
return false, err
}
var latestArchives = make([]model.Meter04KV, 0)
err = tx.NewSelect().Model(&latestArchives).
Where("park_id = ?", report.ParkId).
Where("enabled = ?", true).
Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
for _, meter := range latestArchives {
record, has := lo.Find(currentRecords, func(rec *model.EndUserDetail) bool {
return rec.ParkId == meter.ParkId && rec.MeterId == meter.Code
})
if has {
record.CustomerName = meter.CustomerName
record.Address = meter.Address
record.Ratio = meter.Ratio
record.ContactName = meter.ContactName
record.ContactPhone = meter.ContactPhone
record.Seq = meter.Seq
record.IsPublicMeter = meter.IsPublicMeter
success, err := g.resetSingleEndUserRecord(
tx, ctx, *record,
"customer_name",
"address",
"ratio",
"contact_name",
"contact_phone",
"seq",
"public_meter",
)
if err != nil {
return success, err
}
} else {
newEndUser := model.EndUserDetail{
ReportId: report.Id,
ParkId: report.ParkId,
MeterId: meter.Code,
Seq: meter.Seq,
Ratio: meter.Ratio,
Address: meter.Address,
CustomerName: meter.CustomerName,
ContactName: meter.ContactName,
ContactPhone: meter.ContactPhone,
IsPublicMeter: meter.IsPublicMeter,
LastPeriodOverall: decimal.Zero,
LastPeriodCritical: decimal.Zero,
LastPeriodPeak: decimal.Zero,
LastPeriodFlat: decimal.Zero,
LastPeriodValley: decimal.Zero,
}
_, err = tx.NewInsert().Model(&newEndUser).Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
}
}
report.StepState.Submeter = false
res, err := tx.NewUpdate().Model(report).
WherePK().
Column("step_state").
Exec(*ctx)
rows, _ := res.RowsAffected()
if err != nil {
tx.Rollback()
}
return rows >= 0, nil
}
func (g _GodModeService) resetEndUserRecords(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
var records = make([]model.EndUserDetail, 0)
err := tx.NewSelect().Model(&records).
Where("report_id = ?", reportId).
Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
for _, u := range records {
success, err := g.resetSingleEndUserRecord(tx, ctx, u)
if err != nil {
return success, err
}
}
var report = new(model.Report)
err = tx.NewSelect().Model(report).
Where("id = ?", reportId).
Scan(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
report.StepState.Submeter = false
res, err := tx.NewUpdate().Model(report).
WherePK().
Column("step_state").
Exec(*ctx)
rows, _ := res.RowsAffected()
if err != nil {
tx.Rollback()
}
return rows >= 0, nil
}
type ReportPeriod struct {
Id string
Period time.Time
}
func (_GodModeService) isTheLatestReport(ctx *context.Context, reportId string) (bool, error) {
var report = new(model.Report)
err := global.DB.NewSelect().Model(report).
Where("id = ?", reportId).
Scan(*ctx)
if err != nil || report == nil {
return false, exceptions.NewNotFoundErrorFromError("指定报表索引未找到,", err)
}
var maxPeriod time.Time
err = global.DB.NewSelect().Model((*model.Report)(nil)).
ColumnExpr("max(?)", bun.Ident("period")).
Where("park_id = ?", report.ParkId).
Scan(*ctx, &maxPeriod)
if err != nil {
return false, err
}
return maxPeriod.Equal(report.Period), nil
}
func (_GodModeService) forceDeleteReport(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) {
_, err := tx.NewDelete().Model((*model.EndUserDetail)(nil)).
Where("report_id = ?", reportId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
_, err = tx.NewDelete().Model((*model.WillDilutedFee)(nil)).
Where("report_id = ?", reportId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
_, err = tx.NewDelete().Model((*model.ReportSummary)(nil)).
Where("report_id = ?", reportId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
_, err = tx.NewDelete().Model((*model.Report)(nil)).
Where("id = ?", reportId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, err
}
return true, nil
}
func (g _GodModeService) ClearReportSummary(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext(12)
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.resetReportSummary(&tx, &ctx, reportId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
func (g _GodModeService) ClearReportMaintenances(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.flushReportMaintenances(&tx, &ctx, reportId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
func (g _GodModeService) ResynchronizeEndUser(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.resynchronizeEndUserArchives(&tx, &ctx, reportId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("end_user_detail")
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
func (g _GodModeService) ResetEndUserRegisterRecords(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext(48)
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.resetEndUserRecords(&tx, &ctx, reportId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("end_user_detail")
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
func (g _GodModeService) ResetReport(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
var result = true
r, err := g.resetEndUserRecords(&tx, &ctx, reportId)
if err != nil {
return false, err
}
result = result && r
r, err = g.flushReportMaintenances(&tx, &ctx, reportId)
if err != nil {
return false, err
}
result = result && r
r, err = g.resetReportSummary(&tx, &ctx, reportId)
if err != nil {
return false, err
}
result = result && r
r, err = g.resetReportIndex(&tx, &ctx, reportId)
if err != nil {
return false, err
}
result = result && r
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("end_user_detail")
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
func (g _GodModeService) DeleteReport(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
isLatest, err := g.isTheLatestReport(&ctx, reportId)
if err != nil {
return false, err
}
if !isLatest {
return false, exceptions.NewImproperOperateError("不能删除非最新期数的报表。")
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.forceDeleteReport(&tx, &ctx, reportId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("end_user_detail")
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return result, nil
}
// 从此处开始为删除园区相关的内容部分
func (_GodModeService) deleteSpecificMaintenance(tx *bun.Tx, ctx *context.Context, parkId, maintenanceId string) (bool, error) {
res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)).
Where("park_id = ?", parkId).
Where("id = ?", maintenanceId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, nil
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
return rows >= 0, err
}
}
func (_GodModeService) deleteAllMaintenance(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) {
res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)).
Where("park_id = ?", parkId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, nil
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
return rows >= 0, err
}
}
func (_GodModeService) deleteAllMeters(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) {
res, err := tx.NewDelete().Model((*model.Meter04KV)(nil)).
Where("park_id = ?", parkId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, nil
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
return rows >= 0, err
}
}
func (_GodModeService) deletePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) {
res, err := tx.NewDelete().Model((*model.Park)(nil)).
Where("id = ?", parkId).
Exec(*ctx)
if err != nil {
tx.Rollback()
return false, nil
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
return rows >= 0, err
}
}
func (g _GodModeService) RemoveSpecificMaintenance(parkId, maintenanceId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.deleteSpecificMaintenance(&tx, &ctx, parkId, maintenanceId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", maintenanceId))
return result, nil
}
func (g _GodModeService) RemoveAllMaintenance(parkId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.deleteAllMaintenance(&tx, &ctx, parkId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("maintenance_fee")
return result, nil
}
func (g _GodModeService) RemoveAllMeters(parkId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.deleteAllMeters(&tx, &ctx, parkId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation("meter_04kv")
return result, nil
}
func (g _GodModeService) erasePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) {
var reportIds = make([]string, 0)
err := tx.NewSelect().Model((*model.Report)(nil)).
Where("park_id = ?", parkId).
Column("id").
Scan(*ctx, &reportIds)
if err != nil {
tx.Rollback()
return false, err
}
var result = true
for _, id := range reportIds {
r, err := g.forceDeleteReport(tx, ctx, id)
if err != nil {
return false, err
}
result = result && r
}
r, err := g.deleteAllMaintenance(tx, ctx, parkId)
if err != nil {
return false, err
}
result = result && r
r, err = g.deleteAllMeters(tx, ctx, parkId)
if err != nil {
return false, err
}
result = result && r
r, err = g.deletePark(tx, ctx, parkId)
if err != nil {
return false, err
}
result = result && r
return result, err
}
func (g _GodModeService) RemovePark(parkId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
result, err := g.erasePark(&tx, &ctx, parkId)
if err != nil {
return false, err
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation(fmt.Sprintf("park:%s", parkId))
return result, nil
}
// 从此处开始为删除用户相关的部分
func (g _GodModeService) DeleteUser(userId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return false, err
}
var parkIds = make([]string, 0)
err = tx.NewSelect().Model((*model.Park)(nil)).
Where("user_id = ?", userId).
WhereAllWithDeleted().
Column("id").
Scan(ctx, &parkIds)
if err != nil {
tx.Rollback()
return false, err
}
var result = true
for _, p := range parkIds {
r, err := g.erasePark(&tx, &ctx, p)
if err != nil {
return false, err
}
result = result && r
}
// 删除用户服务计费数据。
res, err := tx.NewDelete().Model((*model.UserCharge)(nil)).
Where("user_id = ?", userId).
Exec(ctx)
if err != nil {
tx.Rollback()
return false, err
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
result = result && (rows >= 0)
}
// 删除用户详细信息数据
res, err = tx.NewDelete().Model((*model.UserDetail)(nil)).
Where("id = ?", userId).
ForceDelete().
Exec(ctx)
if err != nil {
tx.Rollback()
return false, err
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
result = result && (rows >= 0)
}
// 删除用户基本索引数据
res, err = tx.NewDelete().Model((*model.User)(nil)).
Where("id = ?", userId).
Exec(ctx)
if err != nil {
tx.Rollback()
return false, err
}
if rows, err := res.RowsAffected(); err != nil {
tx.Rollback()
return false, err
} else {
result = result && (rows >= 0)
}
err = tx.Commit()
if err != nil {
tx.Rollback()
return false, err
}
cache.AbolishRelation(fmt.Sprintf("user:%s", userId))
cache.AbolishRelation("user")
cache.AbolishRelation("park")
cache.AbolishRelation("report")
cache.AbolishRelation("charge")
return result, nil
}

View File

@@ -2,42 +2,79 @@ package service
import ( import (
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
mapset "github.com/deckarep/golang-set/v2"
"github.com/google/uuid" "github.com/google/uuid"
"xorm.io/builder" "github.com/samber/lo"
"github.com/shopspring/decimal"
"github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _MaintenanceFeeService struct{} type _MaintenanceFeeService struct {
l *zap.Logger
}
var MaintenanceFeeService _MaintenanceFeeService var MaintenanceFeeService = _MaintenanceFeeService{
l: logger.Named("Service", "maintenance"),
}
func (_MaintenanceFeeService) ListMaintenanceFees(pid []string) ([]model.MaintenanceFee, error) { func (_MaintenanceFeeService) ListMaintenanceFees(pid []string, period string, requestPage int) ([]model.MaintenanceFee, int64, error) {
cond := builder.NewCond() conditions := []string{fmt.Sprintf("%d", requestPage)}
conditions = append(conditions, pid...)
conditions = append(conditions, period)
if cachedTotal, err := cache.RetreiveCount("maintenance_fee", conditions...); cachedTotal != -1 && err == nil {
if fees, _ := cache.RetreiveSearch[[]model.MaintenanceFee]("maintenance_fee", conditions...); fees != nil {
return *fees, cachedTotal, nil
}
}
var (
fees = make([]model.MaintenanceFee, 0)
cond = global.DB.NewSelect().Model(&fees)
)
if len(pid) > 0 { if len(pid) > 0 {
cond = cond.And(builder.Eq{"park_id": pid}) cond = cond.Where("park_id in (?)", bun.In(pid))
} else { } else {
return make([]model.MaintenanceFee, 0), exceptions.NewIllegalArgumentsError("必须给定所要请求的至少一个园区", "park_id") return make([]model.MaintenanceFee, 0), 0, exceptions.NewIllegalArgumentsError("必须给定所要请求的至少一个园区", "park_id")
} }
if fees, _ := cache.RetreiveSearch[[]model.MaintenanceFee]("maintenance_fee", pid...); fees != nil { if len(period) > 0 {
return *fees, nil cond = cond.Where("period = ?", period)
} }
var fees = make([]model.MaintenanceFee, 0)
err := global.DBConn.Where(cond).Desc("created_at").Find(&fees) ctx, cancel := global.TimeoutContext()
defer cancel()
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
total, err := cond.Order("period desc", "created_at desc").
Limit(config.ServiceSettings.ItemsPageSize).
Offset(startItem).
ScanAndCount(ctx)
if err != nil { if err != nil {
return make([]model.MaintenanceFee, 0), err return make([]model.MaintenanceFee, 0), 0, fmt.Errorf("附加费查询出现错误,%w", err)
} }
cache.CacheSearch(fees, []string{"maintenance_fee", "park"}, "maintenance_fee", pid...) relations := lo.Map(fees, func(f model.MaintenanceFee, _ int) string {
return fees, nil return fmt.Sprintf("maintenance_fee:%s", f.Id)
})
relations = append(relations, "maintenance_fee", "park")
cache.CacheCount(relations, "maintenance_fee", int64(total), conditions...)
cache.CacheSearch(fees, relations, "maintenance_fee", conditions...)
return fees, int64(total), nil
} }
func (_MaintenanceFeeService) CreateMaintenanceFeeRecord(fee model.MaintenanceFee) error { func (_MaintenanceFeeService) CreateMaintenanceFeeRecord(fee model.MaintenanceFee) error {
ctx, cancel := global.TimeoutContext()
defer cancel()
fee.Id = uuid.New().String() fee.Id = uuid.New().String()
fee.Enabled = true fee.Enabled = true
_, err := global.DBConn.Insert(fee) _, err := global.DB.NewInsert().Model(&fee).Exec(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -46,44 +83,61 @@ func (_MaintenanceFeeService) CreateMaintenanceFeeRecord(fee model.MaintenanceFe
} }
func (_MaintenanceFeeService) ModifyMaintenanceFee(fee model.MaintenanceFee) error { func (_MaintenanceFeeService) ModifyMaintenanceFee(fee model.MaintenanceFee) error {
rows, err := global.DBConn.Table(&model.MaintenanceFee{}).NoAutoCondition().ID(fee.Id).Cols("fee", "memo").Update(fee) ctx, cancel := global.TimeoutContext()
defer cancel()
res, err := global.DB.NewUpdate().Model(&fee).
WherePK().
Column("fee", "memo").
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到匹配的维护费记录。") return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("maintenance_fee") cache.AbolishRelation("maintenance_fee")
cache.AbolishRelation(fmt.Sprintf("maintenance_fee_%s", fee.Id)) cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fee.Id))
return nil return nil
} }
func (_MaintenanceFeeService) ChangeMaintenanceFeeState(fid string, state bool) error { func (_MaintenanceFeeService) ChangeMaintenanceFeeState(fid string, state bool) error {
rows, err := global.DBConn.Table(&model.MaintenanceFee{}).ID(fid).Update(map[string]interface{}{"enabled": state}) ctx, cancel := global.TimeoutContext()
defer cancel()
res, err := global.DB.NewUpdate().Model((*model.MaintenanceFee)(nil)).
Where("id = ?", fid).
Set("enabled = ?", state).
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, err := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到匹配的维护费记录。") return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("maintenance_fee") cache.AbolishRelation("maintenance_fee")
cache.AbolishRelation(fmt.Sprintf("maintenance_fee_%s", fid)) cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid))
return nil return nil
} }
func (_MaintenanceFeeService) DeleteMaintenanceFee(fid string) error { func (_MaintenanceFeeService) DeleteMaintenanceFee(fid string) error {
rows, err := global.DBConn.ID(fid).NoAutoCondition().Delete(new(model.MaintenanceFee)) ctx, cancel := global.TimeoutContext()
defer cancel()
res, err := global.DB.NewDelete().Model((*model.MaintenanceFee)(nil)).
Where("id = ?", fid).
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, err := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到匹配的维护费记录。") return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("maintenance_fee") cache.AbolishRelation("maintenance_fee")
cache.AbolishRelation(fmt.Sprintf("maintenance_fee_%s", fid)) cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid))
return nil return nil
} }
@@ -91,26 +145,151 @@ func (_MaintenanceFeeService) EnsureFeeBelongs(uid, mid string) (bool, error) {
if has, _ := cache.CheckExists("maintenance_fee", mid, uid); has { if has, _ := cache.CheckExists("maintenance_fee", mid, uid); has {
return true, nil return true, nil
} }
var fee = make([]model.MaintenanceFee, 0)
err := global.DBConn. ctx, cancel := global.TimeoutContext()
ID(mid).Limit(1).Find(&fee) defer cancel()
parks := make([]model.Park, 0)
err := global.DB.NewSelect().Model(&parks).
Relation("MaintenanceFees").
Where("p.user_id = ?", uid).
Scan(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
if len(fee) == 0 { exists := lo.Reduce(parks, func(acc bool, elem model.Park, _ int) bool {
return false, exceptions.NewNotFoundError("指定维护费条目未找到。") for _, e := range elem.MaintenanceFees {
if e.Id == mid {
return acc || true
} }
var park = make([]model.Park, 0)
err = global.DBConn.
ID(fee[0].ParkId).Limit(1).Find(&park)
if err != nil {
return false, err
} }
if len(park) == 0 { return acc || false
return false, exceptions.NewNotFoundError("指定维护费所属园区未找到。") }, false)
if !exists {
return false, exceptions.NewNotFoundError("指定附加费所属园区未找到。")
} }
if park[0].UserId == uid { cache.CacheExists([]string{fmt.Sprintf("maintenance_fee:%s", mid), "maintenance_fee", "park"}, "maintenance_fee", mid, uid)
cache.CacheExists([]string{"maintenance_fee", "park"}, "maintenance_fee", mid, uid) return exists, nil
} }
return park[0].UserId == uid, nil
type _FeeStat struct {
ParkId string
Period string
Total decimal.Decimal
}
func (f _MaintenanceFeeService) QueryAdditionalCharges(uid, pid, period, keyword string, requestPage int) ([]model.AdditionalCharge, int64, error) {
var (
conditions = []string{fmt.Sprintf("%d", requestPage)}
statFees = make([]_FeeStat, 0)
cond = global.DB.NewSelect().
Model((*model.MaintenanceFee)(nil)).
Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.ExcludeColumn("*")
}).
Relation("Park.Enterprise", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.ExcludeColumn("*")
}).
Where("m.enabled = ?", true)
)
if len(uid) > 0 {
cond = cond.Where("park__enterprise.id = ?", uid)
conditions = append(conditions, uid)
} else {
conditions = append(conditions, "_")
}
if len(pid) > 0 {
cond = cond.Where("park.id = ?", pid)
conditions = append(conditions, pid)
} else {
conditions = append(conditions, "_")
}
if len(period) > 0 {
cond = cond.Where("m.period = ?", period)
conditions = append(conditions, period)
} else {
conditions = append(conditions, "_")
}
if len(keyword) > 0 {
keywordCond := "%" + keyword + "%"
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Where("park__enterprise.name like ?", keywordCond).
WhereOr("park__enterprise.abbr like ?", keywordCond).
WhereOr("park.name like ?", keywordCond).
WhereOr("park.abbr like ?", keywordCond).
WhereOr("park.address like ?", keywordCond)
})
conditions = append(conditions, keyword)
} else {
conditions = append(conditions, "_")
}
if cachedTotal, err := cache.RetreiveCount("additional_charge", conditions...); cachedTotal != -1 && err == nil {
if cachedData, _ := cache.RetreiveSearch[[]model.AdditionalCharge]("additional_charge", conditions...); cachedData != nil {
return *cachedData, cachedTotal, nil
}
}
ctx, cancel := global.TimeoutContext(24)
defer cancel()
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
total, err := cond.ColumnExpr("sum(?) as total", bun.Ident("fee")).
Column("park_id", "period").
Group("park_id", "period").
Order("period desc").
Limit(config.ServiceSettings.ItemsPageSize).
Offset(startItem).
ScanAndCount(ctx, &statFees)
if err != nil {
return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取附加费统计信息出现错误,%w", err)
}
parkIds := lo.Reduce(
statFees,
func(acc mapset.Set[string], elem _FeeStat, _ int) mapset.Set[string] {
acc.Add(elem.ParkId)
return acc
},
mapset.NewSet[string](),
)
parks := make([]model.Park, 0)
if len(parkIds.ToSlice()) > 0 {
err = global.DB.NewSelect().Model(&parks).Relation("Enterprise").
Where("p.id in (?)", bun.In(parkIds.ToSlice())).
Scan(ctx)
if err != nil {
return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取园区信息出现错误,%w", err)
}
}
assembledStat := lo.Reduce(
statFees,
func(acc []model.AdditionalCharge, elem _FeeStat, _ int) []model.AdditionalCharge {
park, has := lo.Find(parks, func(p model.Park) bool {
return p.Id == elem.ParkId
})
f.l.Debug("Park detection.", zap.Bool("has", has), zap.Any("park", park))
if has {
if !park.Area.Valid || park.Area.Decimal.Equal(decimal.Zero) {
return acc
}
price := elem.Total.Div(park.Area.Decimal).RoundBank(8)
return append(acc, model.AdditionalCharge{
ParkId: elem.ParkId,
Period: elem.Period,
Fee: elem.Total,
Price: price,
QuarterPrice: price.Div(decimal.NewFromInt(4)),
SemiAnnualPrice: price.Div(decimal.NewFromInt(2)),
Enterprise: model.FromUserDetail(*park.Enterprise),
Park: park,
})
} else {
return acc
}
},
make([]model.AdditionalCharge, 0),
)
cache.CacheCount([]string{"maintenance_fee"}, "additional_charge", int64(total), conditions...)
cache.CacheSearch(assembledStat, []string{"maintenance_fee"}, "additional_charge", conditions...)
return assembledStat, int64(total), nil
} }

View File

@@ -1,61 +1,73 @@
package service package service
import ( import (
"context"
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/excel" "electricity_bill_calc/excel"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
"strconv" "strconv"
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/samber/lo" "github.com/samber/lo"
"xorm.io/builder" "github.com/uptrace/bun"
"xorm.io/xorm" "go.uber.org/zap"
) )
type _Meter04kVService struct{} type _Meter04kVService struct {
l *zap.Logger
}
var Meter04kVService _Meter04kVService var Meter04kVService = _Meter04kVService{
l: logger.Named("Service", "Meter04KV"),
}
func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]model.Meter04KV, int64, error) { func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]model.Meter04KV, int64, error) {
var condition = make([]string, 0) var (
cond := builder.NewCond().And(builder.Eq{"park_id": park}) condition = make([]string, 0)
meters = make([]model.Meter04KV, 0)
)
cond := global.DB.NewSelect().Model(&meters).
Where("park_id = ?", park)
condition = append(condition, park, strconv.Itoa(page)) condition = append(condition, park, strconv.Itoa(page))
if len(keyword) > 0 { if len(keyword) > 0 {
cond = cond.And( keywordCond := "%" + keyword + "%"
builder.Like{"address", keyword}. cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
Or(builder.Like{"customer_name", keyword}). return q.Where("address like ?", keywordCond).
Or(builder.Like{"code", keyword}). WhereOr("code like ?", keywordCond).
Or(builder.Like{"contact_name", keyword}). WhereOr("customer_name like ?", keywordCond).
Or(builder.Like{"contact_phone", keyword})) WhereOr("contact_name like ?", keywordCond).
WhereOr("contact_phone like ?", keywordCond)
})
condition = append(condition, keyword) condition = append(condition, keyword)
} }
var ( if cachedTotal, err := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 && err == nil {
total int64
err error
)
if cachedTotal, _ := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 {
total = cachedTotal
} else {
total, err = global.DBConn.Where(cond).NoAutoCondition().Count(new(model.Meter04KV))
if err != nil {
return make([]model.Meter04KV, 0), -1, err
}
cache.CacheCount([]string{"meter_04kv", "park"}, "meter_04kv", total, condition...)
}
var meters = make([]model.Meter04KV, 0)
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
if cachedMeters, _ := cache.RetreiveSearch[[]model.Meter04KV]("meter_04kv", condition...); cachedMeters != nil { if cachedMeters, _ := cache.RetreiveSearch[[]model.Meter04KV]("meter_04kv", condition...); cachedMeters != nil {
return *cachedMeters, total, nil return *cachedMeters, cachedTotal, nil
} }
err = global.DBConn. }
Where(cond).
Limit(config.ServiceSettings.ItemsPageSize, startItem). ctx, cancel := global.TimeoutContext()
Find(&meters) defer cancel()
cache.CacheSearch(meters, []string{"meter_04kv", "park"}, "meter_04kv", condition...) startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
return meters, total, err total, err := cond.
Order("seq asc", "code asc").
Limit(config.ServiceSettings.ItemsPageSize).
Offset(startItem).
ScanAndCount(ctx)
relations := lo.Map(meters, func(m model.Meter04KV, _ int) string {
return fmt.Sprintf("meter_04kv:%s:%s", m.ParkId, m.Code)
})
relations = append(relations, "meter_04kv", "park")
cache.CacheCount(relations, "meter_04kv", int64(total), condition...)
cache.CacheSearch(meters, relations, "meter_04kv", condition...)
return meters, int64(total), err
} }
func (_Meter04kVService) Get04kVMeterDetail(park, code string) (*model.Meter04KV, error) { func (_Meter04kVService) Get04kVMeterDetail(park, code string) (*model.Meter04KV, error) {
@@ -63,19 +75,21 @@ func (_Meter04kVService) Get04kVMeterDetail(park, code string) (*model.Meter04KV
return cachedMeter, nil return cachedMeter, nil
} }
var meter = new(model.Meter04KV) var meter = new(model.Meter04KV)
has, err := global.DBConn.Where(builder.Eq{"code": code, "park_id": park}).NoAutoCondition().Get(meter) ctx, cancel := global.TimeoutContext()
defer cancel()
err := global.DB.NewSelect().Model(meter).
Where("code = ?", code).
Where("park_id = ?", park).
Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !has { cache.CacheEntity(meter, []string{fmt.Sprintf("meter_04kv:%s:%s", park, code), "park"}, "meter_04kv", fmt.Sprintf("%s_%s", park, code))
return nil, nil
}
cache.CacheEntity(meter, []string{fmt.Sprintf("meter_04kv_%s_%s", park, code), "park"}, "meter_04kv", fmt.Sprintf("%s_%s", park, code))
return meter, nil return meter, nil
} }
func (_Meter04kVService) insertNewMeter(tx *xorm.Session, meter model.Meter04KV) error { func (_Meter04kVService) insertNewMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error {
_, err := tx.Insert(meter) _, err := tx.NewInsert().Model(&meter).Exec(*ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
} }
@@ -83,28 +97,28 @@ func (_Meter04kVService) insertNewMeter(tx *xorm.Session, meter model.Meter04KV)
return err return err
} }
func (_Meter04kVService) updateMeter(tx *xorm.Session, meter model.Meter04KV) error { func (_Meter04kVService) updateMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error {
_, err := tx. _, err := tx.NewUpdate().Model(&meter).
Where(builder.Eq{"code": meter.Code, "park_id": meter.ParkId}). Where("code = ?", meter.Code).
Cols("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "dilute", "enabled"). Where("park_id = ?", meter.ParkId).
NoAutoCondition(). Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled").
Update(meter) Exec(*ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
} }
cache.AbolishRelation("meter_04kv") cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
cache.AbolishRelation(fmt.Sprintf("meter_04kv_%s_%s", meter.ParkId, meter.Code))
return err return err
} }
func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error { func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
if err := tx.Begin(); err != nil { defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
defer tx.Close()
err := m.insertNewMeter(tx, meter) err = m.insertNewMeter(&tx, &ctx, meter)
if err != nil { if err != nil {
return err return err
} }
@@ -114,18 +128,19 @@ func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error {
return err return err
} }
cache.AbolishRelation("meter_04kv") cache.AbolishRelation("meter_04kv")
cache.AbolishRelation(fmt.Sprintf("meter_04kv_%s_%s", meter.ParkId, meter.Code)) cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
return nil return nil
} }
func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error { func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
if err := tx.Begin(); err != nil { defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
defer tx.Close()
err := m.updateMeter(tx, *meter) err = m.updateMeter(&tx, &ctx, *meter)
if err != nil { if err != nil {
return err return err
} }
@@ -134,8 +149,7 @@ func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error {
tx.Rollback() tx.Rollback()
return err return err
} }
cache.AbolishRelation("meter_04kv") cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
cache.AbolishRelation(fmt.Sprintf("meter_04kv_%s_%s", meter.ParkId, meter.Code))
return nil return nil
} }
@@ -161,27 +175,49 @@ func (m _Meter04kVService) BatchCreateMeter(meters []model.Meter04KV) error {
} }
parkId, _ := parkIds.Pop() parkId, _ := parkIds.Pop()
ctx, cancel := global.TimeoutContext(120)
defer cancel()
allMeterCodes := make([]string, 0) allMeterCodes := make([]string, 0)
err := global.DBConn.Table(&model.Meter04KV{}).Where(builder.Eq{"park_id": parkId}).Select("code").Find(&allMeterCodes) err := global.DB.NewSelect().Model((*model.Meter04KV)(nil)).
Where("park_id = ?", parkId).
Column("code").
Scan(ctx, &allMeterCodes)
if err != nil { if err != nil {
return err return err
} }
meterCodes := mapset.NewSet(allMeterCodes...) meterCodes := mapset.NewSet(allMeterCodes...)
tx := global.DBConn.NewSession() tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err := tx.Begin(); err != nil { if err != nil {
return err return err
} }
defer tx.Close()
var (
updates = make([]model.Meter04KV, 0)
inserts = make([]model.Meter04KV, 0)
)
for _, meter := range meters { for _, meter := range meters {
var err error
if meterCodes.Contains(meter.Code) { if meterCodes.Contains(meter.Code) {
err = m.updateMeter(tx, meter) updates = append(updates, meter)
} else { } else {
err = m.insertNewMeter(tx, meter) inserts = append(inserts, meter)
} }
}
if len(updates) > 0 {
_, err = tx.NewUpdate().Model(&updates).
Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled").
Bulk().
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback()
return err
}
}
if len(inserts) > 0 {
_, err = tx.NewInsert().Model(&inserts).Exec(ctx)
if err != nil {
tx.Rollback()
return err return err
} }
} }

View File

@@ -4,18 +4,26 @@ import (
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
"xorm.io/builder" "github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _ParkService struct{} type _ParkService struct {
l *zap.Logger
}
var ParkService _ParkService var ParkService = _ParkService{
l: logger.Named("Service", "Park"),
}
func (_ParkService) SaveNewPark(park model.Park) error { func (_ParkService) SaveNewPark(park model.Park) error {
_, err := global.DBConn.Insert(park) ctx, cancel := global.TimeoutContext()
defer cancel()
_, err := global.DB.NewInsert().Model(&park).Exec(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -24,68 +32,90 @@ func (_ParkService) SaveNewPark(park model.Park) error {
} }
func (_ParkService) UpdateParkInfo(park *model.Park) error { func (_ParkService) UpdateParkInfo(park *model.Park) error {
rows, err := global.DBConn. ctx, cancel := global.TimeoutContext()
Where(builder.Eq{"id": park.Id, "user_id": park.UserId}). defer cancel()
Cols("name", "abbr", "region", "address", "contact", "phone", "capacity", "tenement_quantity", "category", "meter_04kv_type"). res, err := global.DB.NewUpdate().Model(park).
Update(park) Where("id = ?", park.Id).
Where("user_id = ?", park.UserId).
Column("name", "abbr", "region", "area", "address", "contact", "phone", "capacity", "tenement_quantity", "category", "meter_04kv_type").
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到符合条件的园区。") return exceptions.NewNotFoundError("未能找到符合条件的园区。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("park") cache.AbolishRelation(fmt.Sprintf("park:%s", park.Id))
cache.AbolishRelation(fmt.Sprintf("park_%s", park.Id))
return nil return nil
} }
func (_ParkService) ChangeParkState(uid, pid string, state bool) error { func (_ParkService) ChangeParkState(uid, pid string, state bool) error {
rows, err := global.DBConn. ctx, cancel := global.TimeoutContext()
Table(&model.Park{}). defer cancel()
Where(builder.Eq{"id": pid, "user_id": uid}). res, err := global.DB.NewUpdate().Model((*model.Park)(nil)).
Update(map[string]interface{}{"enabled": state}) Where("id = ?", pid).
Where("user_id = ?", uid).
Set("enabled = ?", state).
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到符合条件的园区。") return exceptions.NewNotFoundError("未能找到符合条件的园区。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("park") cache.AbolishRelation(fmt.Sprintf("park:%s", pid))
cache.AbolishRelation(fmt.Sprintf("park_%s", pid))
return nil return nil
} }
func (_ParkService) DeletePark(uid, pid string) error { func (_ParkService) DeletePark(uid, pid string) error {
rows, err := global.DBConn. ctx, cancel := global.TimeoutContext()
Where(builder.Eq{"id": pid, "user_id": uid}). defer cancel()
Delete(&model.Park{}) res, err := global.DB.NewDelete().Model((*model.Park)(nil)).
Where("id = ?", pid).
Where("user_id = ?", uid).
Exec(ctx)
if err != nil { if err != nil {
if rows == 0 { if rows, _ := res.RowsAffected(); rows == 0 {
return exceptions.NewNotFoundError("未能找到符合条件的园区。") return exceptions.NewNotFoundError("未能找到符合条件的园区。")
} else { } else {
return err return err
} }
} }
cache.AbolishRelation("park") cache.AbolishRelation("park")
cache.AbolishRelation(fmt.Sprintf("park_%s", pid)) cache.AbolishRelation(fmt.Sprintf("park:%s", pid))
return nil return nil
} }
func (_ParkService) ListAllParkBelongsTo(uid string) ([]model.Park, error) { func (_ParkService) ListAllParkBelongsTo(uid, keyword string) ([]model.Park, error) {
if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid); parks != nil { if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid, keyword); parks != nil {
return *parks, nil return *parks, nil
} }
parks := make([]model.Park, 0) parks := make([]model.Park, 0)
err := global.DBConn. cond := global.DB.NewSelect().Model(&parks).
Where(builder.Eq{"user_id": uid}). Where("user_id = ?", uid)
NoAutoCondition(). if len(keyword) > 0 {
Find(&parks) keywordCond := "%" + keyword + "%"
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Where("name like ?", keywordCond).
WhereOr("abbr like ?", keywordCond).
WhereOr("address like ?", keywordCond).
WhereOr("contact like ?", keywordCond).
WhereOr("phone like ?", keywordCond)
})
}
ctx, cancel := global.TimeoutContext()
defer cancel()
err := cond.Scan(ctx)
if err != nil { if err != nil {
return make([]model.Park, 0), err return make([]model.Park, 0), err
} }
cache.CacheSearch(parks, []string{"park"}, "park", "belong", uid) relations := []string{"park"}
for _, p := range parks {
relations = append(relations, fmt.Sprintf("park:%s", p.Id))
}
cache.CacheSearch(parks, relations, "park", "belong", uid, keyword)
return parks, nil return parks, nil
} }
@@ -93,15 +123,16 @@ func (_ParkService) FetchParkDetail(pid string) (*model.Park, error) {
if park, _ := cache.RetreiveEntity[model.Park]("park", pid); park != nil { if park, _ := cache.RetreiveEntity[model.Park]("park", pid); park != nil {
return park, nil return park, nil
} }
var park = &model.Park{} ctx, cancel := global.TimeoutContext()
has, err := global.DBConn.ID(pid).NoAutoCondition().Get(park) defer cancel()
var park = new(model.Park)
err := global.DB.NewSelect().Model(park).
Where("id = ?", pid).
Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, exceptions.NewNotFoundErrorFromError("未找到符合条件的园区记录。", err)
} }
if !has { cache.CacheEntity(*park, []string{fmt.Sprintf("park:%s", pid)}, "park", pid)
return nil, exceptions.NewNotFoundError("未找到符合条件的园区记录。")
}
cache.CacheEntity(park, []string{"park"}, "park", pid)
return park, nil return park, nil
} }
@@ -109,9 +140,14 @@ func (_ParkService) EnsurePark(uid, pid string) (bool, error) {
if has, _ := cache.CheckExists("park", pid, uid); has { if has, _ := cache.CheckExists("park", pid, uid); has {
return has, nil return has, nil
} }
has, err := global.DBConn.Table(&model.Park{}).Where(builder.Eq{"user_id": uid, "id": pid}).Exist() ctx, cancel := global.TimeoutContext()
defer cancel()
has, err := global.DB.NewSelect().Model((*model.Park)(nil)).
Where("id = ?", pid).
Where("user_id = ?", uid).
Exists(ctx)
if has { if has {
cache.CacheExists([]string{"park"}, "park", pid, uid) cache.CacheExists([]string{fmt.Sprintf("park:%s", pid)}, "park", pid, uid)
} }
return has, err return has, err
} }
@@ -120,12 +156,13 @@ func (_ParkService) AllParkIds(uid string) ([]string, error) {
if ids, _ := cache.RetreiveSearch[[]string]("park", "belong", uid); ids != nil { if ids, _ := cache.RetreiveSearch[[]string]("park", "belong", uid); ids != nil {
return *ids, nil return *ids, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
var ids = make([]string, 0) var ids = make([]string, 0)
err := global.DBConn. err := global.DB.NewSelect().Model((*model.Park)(nil)).
Table(new(model.Park)). Where("user_id = ?", uid).
Where(builder.Eq{"user_id": uid}). Column("id").
Cols("id"). Scan(ctx, &ids)
Find(&ids)
if err != nil { if err != nil {
return make([]string, 0), err return make([]string, 0), err
} }

View File

@@ -3,25 +3,37 @@ package service
import ( import (
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt"
"xorm.io/builder" "github.com/samber/lo"
"go.uber.org/zap"
) )
type _RegionService struct{} type _RegionService struct {
l *zap.Logger
}
var RegionService _RegionService var RegionService = _RegionService{
l: logger.Named("Service", "Region"),
}
func (_RegionService) FetchSubRegions(parent string) ([]model.Region, error) { func (_RegionService) FetchSubRegions(parent string) ([]model.Region, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
if regions, _ := cache.RetreiveSearch[[]model.Region]("region", "parent", parent); regions != nil { if regions, _ := cache.RetreiveSearch[[]model.Region]("region", "parent", parent); regions != nil {
return *regions, nil return *regions, nil
} }
regions := make([]model.Region, 0) regions := make([]model.Region, 0)
err := global.DBConn.Where(builder.Eq{"parent": parent}).Asc("code").Find(&regions) err := global.DB.NewSelect().Model(&regions).Where("parent = ?", parent).Order("code asc").Scan(ctx)
if err != nil { if err != nil {
return make([]model.Region, 0), err return make([]model.Region, 0), err
} }
cache.CacheSearch(regions, []string{"region"}, "region", "parent", parent) relationNames := lo.Map(regions, func(r model.Region, index int) string {
return fmt.Sprintf("region:%s", r.Code)
})
cache.CacheSearch(regions, relationNames, "region", "parent", parent)
return regions, err return regions, err
} }
@@ -43,13 +55,16 @@ func (r _RegionService) FetchAllParentRegions(code string) ([]model.Region, erro
} }
func (_RegionService) fetchRegion(code string) (*model.Region, error) { func (_RegionService) fetchRegion(code string) (*model.Region, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
if cachedRegion, _ := cache.RetreiveSearch[model.Region]("region", code); cachedRegion != nil { if cachedRegion, _ := cache.RetreiveSearch[model.Region]("region", code); cachedRegion != nil {
return cachedRegion, nil return cachedRegion, nil
} }
region := new(model.Region) region := new(model.Region)
has, err := global.DBConn.ID(code).NoAutoCondition().Get(region) err := global.DB.NewSelect().Model(region).Where("code = ?", code).Scan(ctx)
if has { if err != nil {
cache.CacheSearch(region, []string{"region"}, "region", code) relationName := fmt.Sprintf("region:%s", code)
cache.CacheSearch(region, []string{relationName}, "region", code)
} }
return _postProcessSingle(region, has, err) return region, err
} }

View File

@@ -1,10 +1,12 @@
package service package service
import ( import (
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"electricity_bill_calc/tools" "electricity_bill_calc/tools"
"fmt" "fmt"
@@ -15,60 +17,74 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"xorm.io/builder" "github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _ReportService struct{} type _ReportService struct {
l *zap.Logger
}
var ReportService _ReportService var ReportService = _ReportService{
l: logger.Named("Service", "Report"),
}
func (_ReportService) FetchParksWithNewestReport(uid string) ([]model.ParkNewestReport, error) { func (_ReportService) FetchParksWithNewestReport(uid string) ([]model.ParkNewestReport, error) {
if cachedParks, _ := cache.RetreiveSearch[[]model.ParkNewestReport]("park_newest_report", uid); cachedParks != nil { if cachedParks, _ := cache.RetreiveSearch[[]model.ParkNewestReport]("park_newest_report", uid); cachedParks != nil {
return *cachedParks, nil return *cachedParks, nil
} }
parks := make([]model.ParkNewestReport, 0)
err := global.DBConn. ctx, cancel := global.TimeoutContext()
Alias("p"). defer cancel()
Join("LEFT", []string{"report", "r"}, "r.park_id=p.id"). parks := make([]model.Park, 0)
Where(builder.Eq{"p.user_id": uid, "p.enabled": true}). err := global.DB.NewSelect().Model(&parks).Relation("Reports").
Find(&parks) Where("user_id = ?", uid).
Where("enabled = ?", true).
Order("created_at asc").
Scan(ctx)
if err != nil { if err != nil {
return make([]model.ParkNewestReport, 0), err return make([]model.ParkNewestReport, 0), err
} }
reducedParks := lo.Reduce( reducedParks := lo.Reduce(
parks, parks,
func(acc map[string]model.ParkNewestReport, elem model.ParkNewestReport, index int) map[string]model.ParkNewestReport { func(acc map[string]model.ParkNewestReport, elem model.Park, index int) map[string]model.ParkNewestReport {
if v, ok := acc[elem.Park.Id]; ok { if _, ok := acc[elem.Id]; !ok {
if elem.Report != nil { newestReport := lo.MaxBy(elem.Reports, func(a, b *model.Report) bool {
if v.Report == nil || (elem.Report.Period.After(v.Report.Period)) { return a.Period.After(b.Period)
acc[elem.Park.Id] = elem })
acc[elem.Id] = model.ParkNewestReport{
Report: newestReport,
Park: elem,
} }
} }
} else {
acc[elem.Park.Id] = elem
}
return acc return acc
}, },
make(map[string]model.ParkNewestReport, 0), make(map[string]model.ParkNewestReport, 0),
) )
cache.CacheSearch(reducedParks, []string{"park", "report"}, "park_newest_report", uid) relations := lo.Map(parks, func(r model.Park, _ int) string {
return fmt.Sprintf("park:%s", r.Id)
})
relations = append(relations, "park", "report")
cache.CacheSearch(reducedParks, relations, "park_newest_report", uid)
return lo.Values(reducedParks), nil return lo.Values(reducedParks), nil
} }
func (_ReportService) IsNewPeriodValid(uid string, period time.Time) (bool, error) { func (_ReportService) IsNewPeriodValid(uid, pid string, period time.Time) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
reports := make([]model.Report, 0) reports := make([]model.Report, 0)
if cachedReport, _ := cache.RetreiveSearch[[]model.Report]("report", "user", uid); cachedReport != nil { if cachedReport, _ := cache.RetreiveSearch[[]model.Report]("report", "user", uid, "park", pid); cachedReport != nil {
reports = *cachedReport reports = *cachedReport
} else { } else {
err := global.DBConn. err := global.DB.NewSelect().Model(&reports).Relation("Park").
Table("report").Alias("r"). Where("park.user_id = ?", uid).
Join("INNER", []string{"park", "p"}, "r.park_id=p.id"). Where("r.park_id = ?", pid).
Where(builder.Eq{"p.user_id": uid}). Scan(ctx)
Find(&reports)
if err != nil { if err != nil {
return false, nil return false, err
} }
cache.CacheSearch(reports, []string{"report", "park"}, "park", "user", uid) cache.CacheSearch(reports, []string{"report", "park"}, "park", "user", uid, "park", pid)
} }
// 检查给定的期数在目前的记录中是否已经存在 // 检查给定的期数在目前的记录中是否已经存在
exists := lo.Reduce( exists := lo.Reduce(
@@ -122,12 +138,15 @@ func (_ReportService) IsNewPeriodValid(uid string, period time.Time) (bool, erro
} }
func (_ReportService) InitializeNewReport(parkId string, period time.Time) (string, error) { func (_ReportService) InitializeNewReport(parkId string, period time.Time) (string, error) {
ctx, cancel := global.TimeoutContext(120)
defer cancel()
periods := make([]model.Report, 0) periods := make([]model.Report, 0)
err := global.DBConn. err := global.DB.NewSelect().Model(&periods).
Table("report"). Where("park_id = ?", parkId).
Where(builder.Eq{"park_id": parkId, "published": true}). Where("published = ?", true).
Asc("period"). Order("period asc").
Find(&periods) Scan(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -146,7 +165,9 @@ func (_ReportService) InitializeNewReport(parkId string, period time.Time) (stri
if maxPublishedReport != nil { if maxPublishedReport != nil {
// 获取上一期的所有户表信息,并获取当前已启用的所有用户 // 获取上一期的所有户表信息,并获取当前已启用的所有用户
lastPeriodCustomers := make([]model.EndUserDetail, 0) lastPeriodCustomers := make([]model.EndUserDetail, 0)
err = global.DBConn.Where(builder.Eq{"report_id": maxPublishedReport.Id}).Find(&lastPeriodCustomers) err = global.DB.NewSelect().Model(&lastPeriodCustomers).
Where("report_id = ?", maxPublishedReport.Id).
Scan(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -162,21 +183,25 @@ func (_ReportService) InitializeNewReport(parkId string, period time.Time) (stri
indexedLastPeriodCustomers = make(map[string]model.EndUserDetail, 0) indexedLastPeriodCustomers = make(map[string]model.EndUserDetail, 0)
} }
currentActivatedCustomers := make([]model.Meter04KV, 0) currentActivatedCustomers := make([]model.Meter04KV, 0)
err = global.DBConn.Where(builder.Eq{"park_id": parkId, "enabled": true}).Find(&currentActivatedCustomers) err = global.DB.NewSelect().Model(&currentActivatedCustomers).
Where("park_id = ?", parkId).
Where("enabled = ?", true).
Scan(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
var parkInfo = new(model.Park) var parkInfo = new(model.Park)
has, err := global.DBConn.ID(parkId).NoAutoCondition().Get(parkInfo) err = global.DB.NewSelect().Model(parkInfo).
if err != nil || !has { Where("id = ?", parkId).
Scan(ctx)
if err != nil || parkInfo == nil {
return "", exceptions.NewNotFoundError(fmt.Sprintf("指定园区未找到, %v", err)) return "", exceptions.NewNotFoundError(fmt.Sprintf("指定园区未找到, %v", err))
} }
// 生成新一期的报表 // 生成新一期的报表
tx := global.DBConn.NewSession() tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err = tx.Begin(); err != nil { if err != nil {
return "", err return "", err
} }
defer tx.Close()
// 插入已经生成的报表索引信息和园区概况信息 // 插入已经生成的报表索引信息和园区概况信息
newReport := model.Report{ newReport := model.Report{
Id: uuid.New().String(), Id: uuid.New().String(),
@@ -191,12 +216,18 @@ func (_ReportService) InitializeNewReport(parkId string, period time.Time) (stri
newReportSummary := model.ReportSummary{ newReportSummary := model.ReportSummary{
ReportId: newReport.Id, ReportId: newReport.Id,
} }
_, err = tx.Insert(newReport, newReportSummary) _, err = tx.NewInsert().Model(&newReport).Exec(ctx)
if err != nil {
tx.Rollback()
return "", err
}
_, err = tx.NewInsert().Model(&newReportSummary).Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return "", err return "", err
} }
// 生成并插入户表信息 // 生成并插入户表信息
var inserts = make([]model.EndUserDetail, 0)
for _, customer := range currentActivatedCustomers { for _, customer := range currentActivatedCustomers {
newEndUser := model.EndUserDetail{ newEndUser := model.EndUserDetail{
ReportId: newReport.Id, ReportId: newReport.Id,
@@ -209,7 +240,6 @@ func (_ReportService) InitializeNewReport(parkId string, period time.Time) (stri
ContactName: customer.ContactName, ContactName: customer.ContactName,
ContactPhone: customer.ContactPhone, ContactPhone: customer.ContactPhone,
IsPublicMeter: customer.IsPublicMeter, IsPublicMeter: customer.IsPublicMeter,
WillDilute: customer.WillDilute,
LastPeriodOverall: decimal.Zero, LastPeriodOverall: decimal.Zero,
LastPeriodCritical: decimal.Zero, LastPeriodCritical: decimal.Zero,
LastPeriodPeak: decimal.Zero, LastPeriodPeak: decimal.Zero,
@@ -223,7 +253,10 @@ func (_ReportService) InitializeNewReport(parkId string, period time.Time) (stri
newEndUser.LastPeriodFlat = lastPeriod.CurrentPeriodFlat newEndUser.LastPeriodFlat = lastPeriod.CurrentPeriodFlat
newEndUser.LastPeriodValley = lastPeriod.CurrentPeriodValley newEndUser.LastPeriodValley = lastPeriod.CurrentPeriodValley
} }
_, err = tx.Insert(newEndUser) inserts = append(inserts, newEndUser)
}
if len(inserts) > 0 {
_, err = tx.NewInsert().Model(&inserts).Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return "", err return "", err
@@ -242,70 +275,80 @@ func (_ReportService) RetreiveReportIndex(rid string) (*model.Report, error) {
if cachedReport, _ := cache.RetreiveEntity[model.Report]("report", rid); cachedReport != nil { if cachedReport, _ := cache.RetreiveEntity[model.Report]("report", rid); cachedReport != nil {
return cachedReport, nil return cachedReport, nil
} }
reports := make([]model.Report, 0) ctx, cancel := global.TimeoutContext()
err := global.DBConn.Where(builder.Eq{"id": rid}).Find(&reports) defer cancel()
var report = new(model.Report)
err := global.DB.NewSelect().Model(report).
Where("id = ?", rid).
Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(reports) > 0 { cache.CacheEntity(report, []string{fmt.Sprintf("report:%s", rid), "park"}, "report", rid)
cache.CacheEntity(reports[0], []string{fmt.Sprintf("report_%s", rid), "park"}, "report", rid) return report, nil
return &reports[0], nil
} else {
return nil, nil
}
} }
func (_ReportService) RetreiveReportSummary(rid string) (*model.ReportSummary, error) { func (_ReportService) RetreiveReportSummary(rid string) (*model.ReportSummary, error) {
if cachedSummary, _ := cache.RetreiveEntity[model.ReportSummary]("report_summary", rid); cachedSummary != nil { if cachedSummary, _ := cache.RetreiveEntity[model.ReportSummary]("report_summary", rid); cachedSummary != nil {
return cachedSummary, nil return cachedSummary, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
var summary = new(model.ReportSummary) var summary = new(model.ReportSummary)
_, err := global.DBConn.ID(rid).NoAutoCondition().Get(summary) err := global.DB.NewSelect().Model(summary).
Where("report_id = ?", rid).
Scan(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cache.CacheEntity(summary, []string{fmt.Sprintf("report_%s", rid), "park"}, "report_summary", rid) cache.CacheEntity(summary, []string{fmt.Sprintf("report:%s", rid), "park"}, "report_summary", rid)
return summary, nil return summary, nil
} }
func (_ReportService) UpdateReportSummary(summary *model.ReportSummary) error { func (_ReportService) UpdateReportSummary(summary *model.ReportSummary) error {
_, err := global.DBConn.ID(summary.ReportId).Cols("overall", "overall_fee", "critical", "critical_fee", "peak", "peak_fee", "valley", "valley_fee", "basic_fee", "adjust_fee").Update(summary) ctx, cancel := global.TimeoutContext()
defer cancel()
_, err := global.DB.NewUpdate().Model(summary).
WherePK().
Column("overall", "overall_fee", "critical", "critical_fee", "peak", "peak_fee", "valley", "valley_fee", "basic_fee", "adjust_fee").
Exec(ctx)
if err == nil { if err == nil {
cache.AbolishRelation(fmt.Sprintf("report_%s", summary.ReportId)) cache.AbolishRelation(fmt.Sprintf("report:%s", summary.ReportId))
} }
return err return err
} }
func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error { func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error {
ctx, cancel := global.TimeoutContext()
defer cancel()
var report = new(model.Report) var report = new(model.Report)
has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(report) err := global.DB.NewSelect().Model(report).Relation("Summary").
Where("r.id = ?", reportId).
Scan(ctx)
if err != nil || report == nil {
return exceptions.NewNotFoundErrorFromError("未找到指定报表", err)
}
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil { if err != nil {
return err return err
} }
if !has {
return exceptions.NewNotFoundError("未找到指定报表") report.Summary.CalculatePrices()
} _, err = tx.NewUpdate().Model(report.Summary).
var summary = new(model.ReportSummary) WherePK().
has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary) Column("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price", "consumption_fee").
if err != nil { Exec(ctx)
return err
}
if !has {
return exceptions.NewNotFoundError("未找到指定报表的园区概况")
}
tx := global.DBConn.NewSession()
if err = tx.Begin(); err != nil {
return err
}
defer tx.Close()
summary.CalculatePrices()
_, err = tx.ID(summary.ReportId).Cols("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price").Update(summary)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
report.StepState.Summary = true report.StepState.Summary = true
_, err = tx.ID(report.Id).Cols("step_state").Update(report) _, err = tx.NewUpdate().Model(report).
WherePK().
Column("step_state").
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
@@ -315,7 +358,7 @@ func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error {
tx.Rollback() tx.Rollback()
return err return err
} }
cache.AbolishRelation(fmt.Sprintf("report_%s", summary.ReportId)) cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
return nil return nil
} }
@@ -323,37 +366,54 @@ func (_ReportService) FetchWillDulutedMaintenanceFees(reportId string) ([]model.
if cachedFees, _ := cache.RetreiveSearch[[]model.WillDilutedFee]("will_diluted_fee", "report", reportId); cachedFees != nil { if cachedFees, _ := cache.RetreiveSearch[[]model.WillDilutedFee]("will_diluted_fee", "report", reportId); cachedFees != nil {
return *cachedFees, nil return *cachedFees, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
fees := make([]model.WillDilutedFee, 0) fees := make([]model.WillDilutedFee, 0)
err := global.DBConn.Where(builder.Eq{"report_id": reportId}).Asc("created_at").Find(&fees) err := global.DB.NewSelect().Model(&fees).
Where("report_id = ?", reportId).
Order("created_at asc").
Scan(ctx)
if err != nil { if err != nil {
return make([]model.WillDilutedFee, 0), nil return make([]model.WillDilutedFee, 0), nil
} }
cache.CacheSearch(fees, []string{"will_diluted_fee", fmt.Sprintf("report_%s", reportId), "park"}, "will_diluted_fee", "report", reportId) relations := lo.Map(fees, func(f model.WillDilutedFee, _ int) string {
return fmt.Sprintf("will_diluted_fee:%s", f.Id)
})
relations = append(relations, fmt.Sprintf("report:will_diluted_fee:%s", reportId), fmt.Sprintf("report:%s", reportId), "park")
cache.CacheSearch(fees, relations, "will_diluted_fee", "report", reportId)
return fees, nil return fees, nil
} }
func (_ReportService) CreateTemporaryWillDilutedMaintenanceFee(fee model.WillDilutedFee) error { func (_ReportService) CreateTemporaryWillDilutedMaintenanceFee(fee model.WillDilutedFee) error {
ctx, cancel := global.TimeoutContext()
defer cancel()
fee.Id = utils.UUIDString() fee.Id = utils.UUIDString()
_, err := global.DBConn.Insert(fee) _, err := global.DB.NewInsert().Model(&fee).Exec(ctx)
cache.AbolishRelation("will_diluted_fee") cache.AbolishRelation(fmt.Sprintf("report:will_diluted_fee:%s", fee.ReportId))
return err return err
} }
func (_ReportService) BatchSaveMaintenanceFee(reportId string, fees []model.WillDilutedFee) error { func (_ReportService) BatchSaveMaintenanceFee(reportId string, fees []model.WillDilutedFee) error {
tx := global.DBConn.NewSession() ctx, cancel := global.TimeoutContext()
if err := tx.Begin(); err != nil { defer cancel()
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return err return err
} }
defer tx.Close()
// 首先删除所有预定义的部分条件是指定报表IDSourceID不为空。 // 首先删除所有预定义的部分条件是指定报表IDSourceID不为空。
cond := builder.Eq{"report_id": reportId}.And(builder.NotNull{"source_id"}) _, err = tx.NewDelete().Model((*model.WillDilutedFee)(nil)).
_, err := tx.Table(new(model.WillDilutedFee)).Where(cond).Delete() Where("report_id = ?", reportId).
Where("source_id is not null").
Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return err return err
} }
// 然后插入新的记录 // 然后插入新的记录
_, err = tx.Insert(fees) _, err = tx.NewInsert().Model(&fees).Exec(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -362,40 +422,69 @@ func (_ReportService) BatchSaveMaintenanceFee(reportId string, fees []model.Will
tx.Rollback() tx.Rollback()
return err return err
} }
cache.AbolishRelation("will_diluted_fee") cache.AbolishRelation(fmt.Sprintf("report:will_diluted_fee:%s", reportId))
return nil return nil
} }
func (_ReportService) UpdateMaintenanceFee(feeId string, updates map[string]interface{}) (err error) { func (_ReportService) UpdateMaintenanceFee(feeId string, updates map[string]interface{}) (err error) {
_, err = global.DBConn.Table(new(model.WillDilutedFee)).ID(feeId).Update(updates) ctx, cancel := global.TimeoutContext()
cache.AbolishRelation("will_diluted_fee") defer cancel()
updates["last_modified_at"] = lo.ToPtr(time.Now())
_, err = global.DB.NewUpdate().Model(&updates).TableExpr("will_diluted_fee").
Where("id = ?", feeId).
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("will_diluted_fee:%s", feeId))
return return
} }
func (_ReportService) DeleteWillDilutedFee(fee string) (err error) { func (_ReportService) DeleteWillDilutedFee(fee string) (err error) {
_, err = global.DBConn.ID(fee).NoAutoCondition().Delete(new(model.WillDilutedFee)) ctx, cancel := global.TimeoutContext()
cache.AbolishRelation("will_diluted_fee") defer cancel()
_, err = global.DB.NewDelete().Model((*model.WillDilutedFee)(nil)).
Where("id = ?", fee).
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("will_diluted_fee:%s", fee))
return return
} }
func (_ReportService) ProgressReportWillDilutedFee(report model.Report) (err error) { func (_ReportService) ProgressReportWillDilutedFee(report model.Report) (err error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
report.StepState.WillDiluted = true report.StepState.WillDiluted = true
_, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) _, err = global.DB.NewUpdate().Model(&report).
cache.AbolishRelation(fmt.Sprintf("report_%s", report.Id)) WherePK().
Column("step_state").
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id))
return return
} }
func (_ReportService) ProgressReportRegisterEndUser(report model.Report) (err error) { func (_ReportService) ProgressReportRegisterEndUser(report model.Report) (err error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
report.StepState.Submeter = true report.StepState.Submeter = true
_, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) _, err = global.DB.NewUpdate().Model(&report).
cache.AbolishRelation(fmt.Sprintf("report_%s", report.Id)) WherePK().
Column("step_state").
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id))
return return
} }
func (_ReportService) ProgressReportCalculate(report model.Report) (err error) { func (_ReportService) ProgressReportCalculate(report model.Report) (err error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
report.StepState.Calculate = true report.StepState.Calculate = true
_, err = global.DBConn.ID(report.Id).Cols("step_state").Update(report) _, err = global.DB.NewUpdate().Model(&report).
cache.AbolishRelation(fmt.Sprintf("report_%s", report.Id)) WherePK().
Column("step_state").
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id))
return return
} }
@@ -403,92 +492,104 @@ func (_ReportService) RetreiveParkEndUserMeterType(reportId string) (int, error)
if cachedType, _ := cache.RetreiveEntity[int]("park_end_user_meter_type", fmt.Sprintf("report_%s", reportId)); cachedType != nil { if cachedType, _ := cache.RetreiveEntity[int]("park_end_user_meter_type", fmt.Sprintf("report_%s", reportId)); cachedType != nil {
return *cachedType, nil return *cachedType, nil
} }
var types = make([]int, 0) ctx, cancel := global.TimeoutContext()
err := global.DBConn. defer cancel()
Table("park").Alias("p").
Join("INNER", []string{"report", "r"}, "r.park_id=p.id"). var mType int
Where(builder.Eq{"r.id": reportId}). err := global.DB.NewSelect().Model((*model.Report)(nil)).
Select("p.meter_04kv_type"). Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery {
Find(&types) return q.Column("meter_04kv_type")
}).
ExcludeColumn("*").
Where("r.id = ?", reportId).
Scan(ctx, &mType)
if err != nil { if err != nil {
return -1, err return -1, err
} }
if len(types) == 0 { cache.CacheEntity(mType, []string{fmt.Sprintf("report:%s", reportId), "park"}, "park_end_user_meter_type", fmt.Sprintf("report_%s", reportId))
return -1, nil return mType, nil
}
cache.CacheEntity(types[0], []string{fmt.Sprintf("report_%s", reportId), "park"}, "park_end_user_meter_type", fmt.Sprintf("report_%s", reportId))
return types[0], nil
} }
func (_ReportService) PublishReport(report model.Report) (err error) { func (_ReportService) PublishReport(report model.Report) (err error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
report.Published = true report.Published = true
report.PublishedAt = lo.ToPtr(time.Now()) report.PublishedAt = lo.ToPtr(time.Now())
report.StepState.Publish = true report.StepState.Publish = true
_, err = global.DBConn.ID(report.Id).Cols("step_state", "published", "published_at").Update(report) _, err = global.DB.NewUpdate().Model(&report).
cache.AbolishRelation("report") WherePK().
cache.AbolishRelation(fmt.Sprintf("report_%s", report.Id)) Column("step_state", "published", "published_at").
Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id))
return return
} }
func (_ReportService) SearchReport(requestUser, requestPark, requestKeyword string, requestPeriod *time.Time, requestPage int) ([]model.JoinedReportForWithdraw, int64, error) { func (_ReportService) SearchReport(requestUser, requestPark, requestKeyword string, requestPeriod *time.Time, requestPage int, onlyPublished bool) ([]model.JoinedReportForWithdraw, int64, error) {
var conditions = make([]string, 0) var (
conditions = make([]string, 0)
reports = make([]model.Report, 0)
cond = global.DB.NewSelect().
Model(&reports).
Relation("Park").Relation("Park.Enterprise")
)
conditions = append(conditions, strconv.Itoa(requestPage)) conditions = append(conditions, strconv.Itoa(requestPage))
cond := builder.NewCond().And(builder.Eq{"r.published": true}) if onlyPublished {
cond = cond.Where("r.published = ?", true)
}
conditions = append(conditions, strconv.FormatBool(onlyPublished))
if len(requestUser) > 0 { if len(requestUser) > 0 {
cond = cond.And(builder.Eq{"u.id": requestUser}) cond = cond.Where("park.user_id = ?", requestUser)
conditions = append(conditions, requestUser) conditions = append(conditions, requestUser)
} }
if len(requestPark) > 0 { if len(requestPark) > 0 {
cond = cond.And(builder.Eq{"p.id": requestPark}) cond = cond.Where("park.id = ?", requestPark)
conditions = append(conditions, requestPark) conditions = append(conditions, requestPark)
} }
if requestPeriod != nil { if requestPeriod != nil {
cond = cond.And(builder.Eq{"r.period": *requestPeriod}) cond = cond.Where("r.period = ?", *requestPeriod)
conditions = append(conditions, strconv.FormatInt(requestPeriod.Unix(), 10)) conditions = append(conditions, strconv.FormatInt(requestPeriod.Unix(), 10))
} }
if len(requestKeyword) > 0 { if len(requestKeyword) > 0 {
cond = cond.And( keywordCond := "%" + requestKeyword + "%"
builder.Like{"u.name", requestKeyword}. cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
Or(builder.Like{"p.name", requestKeyword}). return q.Where("park.name like ?", keywordCond).
Or(builder.Like{"u.abbr", requestKeyword}). WhereOr("park__enterprise.name like ?", keywordCond).
Or(builder.Like{"p.abbr", requestKeyword}). WhereOr("park__enterprise.abbr like ?", keywordCond).
Or(builder.Like{"u.address", requestKeyword}). WhereOr("park.abbr like ?", keywordCond).
Or(builder.Like{"p.address", requestKeyword}), WhereOr("park__enterprise.address like ?", keywordCond).
) WhereOr("park.address like ?", keywordCond)
})
conditions = append(conditions, requestKeyword) conditions = append(conditions, requestKeyword)
} }
var ( if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil {
total int64
err error
)
if cachedTotal, _ := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 {
total = cachedTotal
} else {
total, err := global.DBConn.
Table("report").Alias("r").
Join("INNER", []string{"park", "p"}, "p.id=r.park_id").
Join("INNER", []string{"user_detail", "u"}, "u.id=p.user_id").
Where(cond).
Count()
if err != nil {
return make([]model.JoinedReportForWithdraw, 0), -1, err
}
cache.CacheCount([]string{"report", "park"}, "join_report_for_withdraw", total, conditions...)
}
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
if cachedRecords, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_report_for_withdraw", conditions...); cachedRecords != nil { if cachedRecords, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_report_for_withdraw", conditions...); cachedRecords != nil {
return *cachedRecords, total, nil return *cachedRecords, cachedTotal, nil
} }
}
ctx, cancel := global.TimeoutContext()
defer cancel()
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
total, err := cond.Limit(config.ServiceSettings.ItemsPageSize).
Offset(startItem).
ScanAndCount(ctx)
records := make([]model.JoinedReportForWithdraw, 0) records := make([]model.JoinedReportForWithdraw, 0)
err = global.DBConn. relations := []string{"report", "park"}
Table("report").Alias("r"). for _, r := range reports {
Join("INNER", []string{"park", "p"}, "p.id=r.park_id"). records = append(records, model.JoinedReportForWithdraw{
Join("INNER", []string{"user_detail", "u"}, "u.id=p.user_id"). Report: r,
Where(cond). Park: model.FromPark(*r.Park),
Limit(config.ServiceSettings.ItemsPageSize, startItem). User: model.FromUserDetail(*r.Park.Enterprise),
Find(&records) })
cache.CacheSearch(records, []string{"report", "park"}, "join_report_for_withdraw", conditions...) relations = append(relations, fmt.Sprintf("report:%s", r.Id))
return records, total, err }
cache.CacheCount(relations, "join_report_for_withdraw", int64(total), conditions...)
cache.CacheSearch(records, relations, "join_report_for_withdraw", conditions...)
return records, int64(total), err
} }
func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity, error) { func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity, error) {
@@ -496,131 +597,120 @@ func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity
return cachedPublicity, nil return cachedPublicity, nil
} }
// 资料准备 // 资料准备
var reportIndex = new(model.Report) ctx, cancel := global.TimeoutContext()
has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(reportIndex) defer cancel()
if err != nil || !has {
var report = new(model.Report)
err := global.DB.NewSelect().Model(report).
Relation("Summary").Relation("WillDilutedFees").Relation("EndUsers").
Relation("Park").Relation("Park.Enterprise").
Where("r.id = ?", reportId).
Scan(ctx)
if err != nil {
return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err)
} }
var summary = new(model.ReportSummary)
has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary)
if err != nil || !has {
return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表概览", err)
}
var maintenanceFeeRecords = make([]model.WillDilutedFee, 0)
err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&maintenanceFeeRecords)
if err != nil {
return nil, exceptions.NewNotFoundErrorFromError("未能获取到公示报表对应的待摊薄费用信息", err)
}
var endUserDetails = make([]model.EndUserDetail, 0)
err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&endUserDetails)
if err != nil {
return nil, exceptions.NewNotFoundErrorFromError("未获取到公示报表对应的终端用户抄表信息", err)
}
parkDetail, err := ParkService.FetchParkDetail(reportIndex.ParkId)
if err != nil {
return nil, exceptions.NewNotFoundErrorFromError("未找到公示报表对应的园区信息", err)
}
userDetail, err := UserService.retreiveUserDetail(parkDetail.UserId)
if err != nil {
return nil, exceptions.NewNotFoundErrorFromError("未找到公示报表对应的企业信息", err)
}
// 组合数据 // 组合数据
paidPart := model.PaidPart{ paidPart := model.PaidPart{
Overall: summary.Overall, Overall: report.Summary.Overall,
OverallPrice: summary.OverallPrice.Decimal, OverallPrice: report.Summary.OverallPrice.Decimal,
ConsumptionFee: summary.ConsumptionFee.Decimal, ConsumptionFee: report.Summary.ConsumptionFee.Decimal,
OverallFee: summary.OverallFee, OverallFee: report.Summary.OverallFee,
Critical: decimal.NewNullDecimal(summary.Critical), Critical: decimal.NewNullDecimal(report.Summary.Critical),
CriticalPrice: summary.CriticalPrice, CriticalPrice: report.Summary.CriticalPrice,
CriticalFee: decimal.NewNullDecimal(summary.CriticalFee), CriticalFee: decimal.NewNullDecimal(report.Summary.CriticalFee),
Peak: decimal.NewNullDecimal(summary.Peak), Peak: decimal.NewNullDecimal(report.Summary.Peak),
PeakPrice: summary.PeakPrice, PeakPrice: report.Summary.PeakPrice,
PeakFee: decimal.NewNullDecimal(summary.PeakFee), PeakFee: decimal.NewNullDecimal(report.Summary.PeakFee),
Flat: decimal.NewNullDecimal(summary.Flat), Flat: decimal.NewNullDecimal(report.Summary.Flat),
FlatPrice: summary.FlatPrice, FlatPrice: report.Summary.FlatPrice,
FlatFee: decimal.NewNullDecimal(summary.FlatFee), FlatFee: decimal.NewNullDecimal(report.Summary.FlatFee),
Valley: decimal.NewNullDecimal(summary.Valley), Valley: decimal.NewNullDecimal(report.Summary.Valley),
ValleyPrice: summary.ValleyPrice, ValleyPrice: report.Summary.ValleyPrice,
ValleyFee: decimal.NewNullDecimal(summary.ValleyFee), ValleyFee: decimal.NewNullDecimal(report.Summary.ValleyFee),
BasicFee: summary.BasicFee, BasicFee: report.Summary.BasicFee,
AdjustFee: summary.AdjustFee, AdjustFee: report.Summary.AdjustFee,
} }
endUserSummary := model.EndUserOverallPart{ endUserSummary := model.ConsumptionOverallPart{
Overall: summary.CustomerConsumption.Decimal, Overall: report.Summary.Customers.Consumption.Decimal,
OverallPrice: summary.OverallPrice.Decimal, OverallPrice: report.Summary.OverallPrice.Decimal,
OverallFee: summary.CustomerConsumptionFee.Decimal, ConsumptionFee: report.Summary.Customers.ConsumptionFee.Decimal,
Critical: summary.CustomerConsumptionCritical, OverallFee: report.Summary.Customers.ConsumptionFee.Decimal,
CriticalPrice: summary.CriticalPrice, Critical: report.Summary.Customers.Critical,
CriticalFee: summary.CustomerConsumptionCriticalFee, CriticalPrice: report.Summary.CriticalPrice,
Peak: summary.CustomerConsumptionPeak, CriticalFee: report.Summary.Customers.CriticalFee,
PeakPrice: summary.PeakPrice, Peak: report.Summary.Customers.Peak,
PeakFee: summary.CustomerConsumptionPeakFee, PeakPrice: report.Summary.PeakPrice,
Flat: summary.CustomerConsumptionFlat, PeakFee: report.Summary.Customers.PeakFee,
FlatPrice: summary.FlatPrice, Flat: report.Summary.Customers.Flat,
FlatFee: summary.CustomerConsumptionFlatFee, FlatPrice: report.Summary.FlatPrice,
Valley: summary.CustomerConsumptionValley, FlatFee: report.Summary.Customers.FlatFee,
ValleyPrice: summary.ValleyPrice, Valley: report.Summary.Customers.Valley,
ValleyFee: summary.CustomerConsumptionValleyFee, ValleyPrice: report.Summary.ValleyPrice,
ValleyFee: report.Summary.Customers.ValleyFee,
Proportion: report.Summary.Customers.Proportion.Decimal,
} }
lossPart := model.LossPart{ lossPart := model.LossPart{
Quantity: summary.Loss.Decimal, Quantity: report.Summary.Loss.Decimal,
Price: summary.OverallPrice.Decimal, Price: report.Summary.OverallPrice.Decimal,
ConsumptionFee: summary.LossFee.Decimal, ConsumptionFee: report.Summary.LossFee.Decimal,
Proportion: summary.LossProportion.Decimal, Proportion: report.Summary.LossProportion.Decimal,
AuthorizeQuantity: report.Summary.AuthorizeLoss.Decimal,
AuthorizeConsumptionFee: report.Summary.AuthorizeLossFee.Decimal,
} }
publicSummary := model.PublicConsumptionOverallPart{ publicSummary := model.ConsumptionOverallPart{
Overall: summary.PublicConsumption.Decimal, Overall: report.Summary.Publics.Consumption.Decimal,
OverallPrice: summary.OverallPrice.Decimal, OverallPrice: report.Summary.OverallPrice.Decimal,
OverallFee: summary.PublicConsumptionFee.Decimal, ConsumptionFee: report.Summary.Publics.ConsumptionFee.Decimal,
Critical: summary.PublicConsumptionCritical, OverallFee: report.Summary.Publics.ConsumptionFee.Decimal,
CriticalPrice: summary.CriticalPrice, Critical: report.Summary.Publics.Critical,
CriticalFee: summary.PublicConsumptionCriticalFee, CriticalPrice: report.Summary.CriticalPrice,
Peak: summary.PublicConsumptionPeak, CriticalFee: report.Summary.Publics.CriticalFee,
PeakPrice: summary.PeakPrice, Peak: report.Summary.Publics.Peak,
PeakFee: summary.PublicConsumptionPeakFee, PeakPrice: report.Summary.PeakPrice,
Flat: summary.PublicConsumptionFlat, PeakFee: report.Summary.Publics.PeakFee,
FlatPrice: summary.FlatPrice, Flat: report.Summary.Publics.Flat,
FlatFee: summary.PublicConsumptionFlatFee, FlatPrice: report.Summary.FlatPrice,
Valley: summary.PublicConsumptionValley, FlatFee: report.Summary.Publics.FlatFee,
ValleyPrice: summary.ValleyPrice, Valley: report.Summary.Publics.Valley,
ValleyFee: summary.PublicConsumptionValleyFee, ValleyPrice: report.Summary.ValleyPrice,
ValleyFee: report.Summary.Publics.ValleyFee,
Proportion: report.Summary.Publics.Proportion.Decimal,
} }
otherCollection := model.OtherShouldCollectionPart{ otherCollection := model.OtherShouldCollectionPart{
MaintenanceFee: summary.FinalDilutedOverall, LossFee: report.Summary.AuthorizeLossFee,
BasicFees: summary.BasicFee.Add(summary.AdjustFee), BasicFees: report.Summary.BasicFee.Add(report.Summary.AdjustFee),
} }
finalMaintenance := lossPart.ConsumptionFee.Add(publicSummary.ConsumptionFee).Add(otherCollection.MaintenanceFee.Decimal).Add(otherCollection.BasicFees) finalAdjustFee := lossPart.AuthorizeConsumptionFee.Add(otherCollection.BasicFees)
var maintenancePrice = decimal.Zero var adjustPrice = decimal.Zero
if !endUserSummary.Overall.Equal(decimal.Zero) { if !endUserSummary.Overall.Equal(decimal.Zero) {
maintenancePrice = finalMaintenance.Div(endUserSummary.Overall).RoundBank(8) adjustPrice = finalAdjustFee.Div(endUserSummary.Overall).RoundBank(8)
} }
var maintenanceProportion = decimal.Zero var adjustProportion = decimal.Zero
if !paidPart.OverallFee.Equal(decimal.Zero) || !otherCollection.MaintenanceFee.Decimal.Equal(decimal.Zero) { if !paidPart.OverallFee.Equal(decimal.Zero) {
maintenanceProportion = finalMaintenance.Div(paidPart.OverallFee.Add(otherCollection.MaintenanceFee.Decimal)).RoundBank(8) adjustProportion = finalAdjustFee.Div(paidPart.OverallFee.Add(finalAdjustFee)).RoundBank(8)
}
var priceRatio = decimal.Zero
if !summary.OverallPrice.Decimal.Equal(decimal.Zero) {
priceRatio = maintenancePrice.Div(summary.OverallPrice.Decimal).RoundBank(8)
} }
maintenanceFees := model.MaintenancePart{ maintenanceFees := model.MaintenancePart{
BasicFees: otherCollection.BasicFees, BasicFees: otherCollection.BasicFees,
LossFee: lossPart.ConsumptionFee, LossFee: lossPart.AuthorizeConsumptionFee,
PublicConsumptionFee: publicSummary.ConsumptionFee, AdjustFee: finalAdjustFee,
MaintenanceFee: otherCollection.MaintenanceFee.Decimal, LossProportion: lossPart.Proportion,
FinalMaintenance: finalMaintenance, AdjustPrice: adjustPrice,
MaintenanceProportion: maintenanceProportion, AdjustProportion: adjustProportion,
MaintenancePrice: maintenancePrice, }
PriceRatio: priceRatio, if maintenanceFees.LossProportion.GreaterThan(decimal.NewFromFloat(0.1)) {
maintenanceFees.LossProportion = decimal.NewFromFloat(0.1)
} }
endUsers := lo.Map( endUsers := lo.Map(
endUserDetails, report.EndUsers,
func(elem model.EndUserDetail, index int) model.EndUserSummary { func(elem *model.EndUserDetail, index int) model.EndUserSummary {
return model.EndUserSummary{ return model.EndUserSummary{
CustomerName: elem.CustomerName, CustomerName: elem.CustomerName,
Address: elem.Address, Address: elem.Address,
MeterId: elem.MeterId, MeterId: elem.MeterId,
IsPublicMeter: elem.IsPublicMeter,
Overall: elem.Overall.Decimal, Overall: elem.Overall.Decimal,
OverallPrice: report.Summary.OverallPrice.Decimal,
OverallFee: elem.OverallFee.Decimal, OverallFee: elem.OverallFee.Decimal,
Critical: elem.Critical, Critical: elem.Critical,
CriticalFee: elem.CriticalFee, CriticalFee: elem.CriticalFee,
@@ -628,15 +718,16 @@ func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity
PeakFee: elem.PeakFee, PeakFee: elem.PeakFee,
Valley: elem.Valley, Valley: elem.Valley,
ValleyFee: elem.ValleyFee, ValleyFee: elem.ValleyFee,
Maintenance: elem.FinalDiluted.Decimal, Loss: elem.LossDiluted.Decimal,
LossFee: elem.LossFeeDiluted.Decimal,
} }
}, },
) )
publicity := &model.Publicity{ publicity := &model.Publicity{
Report: *reportIndex, Report: *report,
Park: *parkDetail, Park: *report.Park,
User: *userDetail, User: *report.Park.Enterprise,
Paid: paidPart, Paid: paidPart,
EndUser: endUserSummary, EndUser: endUserSummary,
Loss: lossPart, Loss: lossPart,
@@ -645,7 +736,7 @@ func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity
Maintenance: maintenanceFees, Maintenance: maintenanceFees,
EndUserDetails: endUsers, EndUserDetails: endUsers,
} }
cache.CacheEntity(publicity, []string{fmt.Sprintf("publicity_%s", reportId), "report", "park"}, "publicity", reportId) cache.CacheEntity(publicity, []string{fmt.Sprintf("publicity:%s", reportId), fmt.Sprintf("report:%s", reportId), "report", "park"}, "publicity", reportId)
return publicity, nil return publicity, nil
} }

View File

@@ -3,70 +3,83 @@ package service
import ( import (
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"github.com/samber/lo" "github.com/samber/lo"
"xorm.io/builder" "github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _StatisticsService struct{} type _StatisticsService struct {
l *zap.Logger
}
var StatisticsService _StatisticsService var StatisticsService = _StatisticsService{
l: logger.Named("Service", "Stat"),
}
func (_StatisticsService) EnabledEnterprises() (int64, error) { func (_StatisticsService) EnabledEnterprises() (int64, error) {
if cachedCount, _ := cache.RetreiveCount("enabled_ent"); cachedCount != -1 { if cachedCount, err := cache.RetreiveCount("enabled_ent"); cachedCount != -1 && err == nil {
return cachedCount, nil return cachedCount, nil
} }
c, err := global.DBConn. ctx, cancel := global.TimeoutContext()
Table(new(model.User)). defer cancel()
Where(builder.Eq{"type": 0, "enabled": true}).
Count() c, err := global.DB.NewSelect().Model((*model.User)(nil)).
Where("type = ?", model.USER_TYPE_ENT).
Where("enabled = ?", true).
Count(ctx)
if err == nil { if err == nil {
cache.CacheCount([]string{"user"}, "enabled_ent", c) cache.CacheCount([]string{"user"}, "enabled_ent", int64(c))
} }
return c, err return int64(c), err
} }
func (_StatisticsService) EnabledParks(userIds ...string) (int64, error) { func (_StatisticsService) EnabledParks(userIds ...string) (int64, error) {
if cachedParks, _ := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 { if cachedParks, err := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 && err == nil {
return cachedParks, nil return cachedParks, nil
} }
cond := builder.NewCond().And(builder.Eq{"enabled": true}) ctx, cancel := global.TimeoutContext()
defer cancel()
query := global.DB.NewSelect().Model((*model.Park)(nil)).
Where("enabled = ?", true)
if len(userIds) > 0 { if len(userIds) > 0 {
cond = cond.And(builder.Eq{"user_id": userIds}) query = query.Where("user_id in (?)", bun.In(userIds))
} }
c, err := global.DBConn. c, err := query.Count(ctx)
Table(new(model.Park)).
Where(cond).
Count()
if err == nil { if err == nil {
cache.CacheCount([]string{"user", "park"}, "enabled_parks", c, userIds...) cache.CacheCount([]string{"user", "park"}, "enabled_parks", int64(c), userIds...)
} }
return c, err return int64(c), err
} }
func (_StatisticsService) ParksNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) { func (_StatisticsService) ParksNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) {
if cachedState, _ := cache.RetreiveSearch[[]model.ParkPeriodStatistics]("park_period_stat", userIds...); cachedState != nil { if cachedState, _ := cache.RetreiveSearch[[]model.ParkPeriodStatistics]("park_period_stat", userIds...); cachedState != nil {
return *cachedState, nil return *cachedState, nil
} }
cond := builder.NewCond().And(builder.Eq{"p.enabled": true, "r.published": true}) ctx, cancel := global.TimeoutContext()
defer cancel()
query := global.DB.NewSelect().Model((*model.Report)(nil)).
Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Column("id", "name")
}).
Where("park.enabled = ?", true).
Where("r.published = ?", true)
if len(userIds) > 0 { if len(userIds) > 0 {
cond = cond.And(builder.Eq{"p.user_id": userIds}) query = query.Where("park.user_id in (?)", bun.In(userIds))
} }
parks := make([]model.ParkPeriodStatistics, 0) parks := make([]model.ParkPeriodStatistics, 0)
groupedParks := make(map[string]model.ParkPeriodStatistics, 0) groupedParks := make(map[string]model.ParkPeriodStatistics, 0)
err := global.DBConn. err := query.Column("period").Scan(ctx, &parks)
Table("park").Alias("p").
Join("LEFT", []string{"report", "r"}, "r.park_id=p.id").
Where(cond).
Cols("p.id", "p.name", "r.period").
Find(&parks)
if err != nil { if err != nil {
return make([]model.ParkPeriodStatistics, 0), err return make([]model.ParkPeriodStatistics, 0), err
} }
for _, p := range parks { for _, p := range parks {
if c, ok := groupedParks[p.Id]; ok { if c, ok := groupedParks[p.Id]; ok {
if c.Period != nil && p.Period != nil && p.Period.After(*c.Period) { if c.Period != nil && p.Period != nil && p.Period.After(c.Period.Time) {
groupedParks[p.Id] = p groupedParks[p.Id] = p
} }
if c.Period == nil && p.Period != nil { if c.Period == nil && p.Period != nil {

View File

@@ -1,10 +1,12 @@
package service package service
import ( import (
"database/sql"
"electricity_bill_calc/cache" "electricity_bill_calc/cache"
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"electricity_bill_calc/tools" "electricity_bill_calc/tools"
"fmt" "fmt"
@@ -13,12 +15,17 @@ import (
"github.com/fufuok/utils" "github.com/fufuok/utils"
"github.com/google/uuid" "github.com/google/uuid"
"xorm.io/builder" "github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _UserService struct{} type _UserService struct {
l *zap.Logger
}
var UserService _UserService var UserService = _UserService{
l: logger.Named("Service", "User"),
}
func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*model.Session, error) { func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*model.Session, error) {
user, err := u.findUserWithCredentialsByUsername(username) user, err := u.findUserWithCredentialsByUsername(username)
@@ -30,14 +37,14 @@ func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*mo
return nil, exceptions.NewAuthenticationError(404, "用户不存在。") return nil, exceptions.NewAuthenticationError(404, "用户不存在。")
} }
if user.Type != 0 { if user.Type != 0 {
return nil, exceptions.NewAuthenticationError(401, "用户类型不正确。") return nil, exceptions.NewAuthenticationError(400, "用户类型不正确。")
} }
if !user.Enabled { if !user.Enabled {
return nil, exceptions.NewAuthenticationError(401, "用户已被禁用。") return nil, exceptions.NewAuthenticationError(403, "用户已被禁用。")
} }
hashedPassword := utils.Sha512Hex(password) hashedPassword := utils.Sha512Hex(password)
if hashedPassword != user.Password { if hashedPassword != user.Password {
return nil, exceptions.NewAuthenticationError(401, "用户凭据不正确。") return nil, exceptions.NewAuthenticationError(402, "用户凭据不正确。")
} }
if user.ResetNeeded { if user.ResetNeeded {
authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。")
@@ -45,8 +52,8 @@ func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*mo
return nil, authErr return nil, authErr
} }
userDetial, _ := u.retreiveUserDetail(user.Id) userDetial, _ := u.retreiveUserDetail(user.Id)
if userDetial.ServiceExpiration.Before(time.Now()) { if userDetial.ServiceExpiration.Time.Before(time.Now()) {
return nil, exceptions.NewAuthenticationError(401, "用户服务期限已过。") return nil, exceptions.NewAuthenticationError(406, "用户服务期限已过。")
} }
session := &model.Session{ session := &model.Session{
Token: uuid.New().String(), Token: uuid.New().String(),
@@ -72,14 +79,14 @@ func (u _UserService) ProcessManagementUserLogin(username, password string) (*mo
return nil, exceptions.NewAuthenticationError(404, "用户不存在。") return nil, exceptions.NewAuthenticationError(404, "用户不存在。")
} }
if user.Type != 1 && user.Type != 2 { if user.Type != 1 && user.Type != 2 {
return nil, exceptions.NewAuthenticationError(401, "用户类型不正确。") return nil, exceptions.NewAuthenticationError(400, "用户类型不正确。")
} }
if !user.Enabled { if !user.Enabled {
return nil, exceptions.NewAuthenticationError(401, "用户已被禁用。") return nil, exceptions.NewAuthenticationError(403, "用户已被禁用。")
} }
hashedPassword := utils.Sha512Hex(password) hashedPassword := utils.Sha512Hex(password)
if hashedPassword != user.Password { if hashedPassword != user.Password {
return nil, exceptions.NewAuthenticationError(401, "用户凭据不正确。") return nil, exceptions.NewAuthenticationError(402, "用户凭据不正确。")
} }
if user.ResetNeeded { if user.ResetNeeded {
authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。")
@@ -106,17 +113,18 @@ func (u _UserService) InvalidUserPassword(uid string) (string, error) {
if user == nil && err != nil { if user == nil && err != nil {
return "", exceptions.NewNotFoundError("指定的用户不存在。") return "", exceptions.NewNotFoundError("指定的用户不存在。")
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
verifyCode := tools.RandStr(10) verifyCode := tools.RandStr(10)
user.Password = utils.Sha512Hex(verifyCode) user.Password = utils.Sha512Hex(verifyCode)
user.ResetNeeded = true user.ResetNeeded = true
affected, err := global.DBConn.ID(uid).Cols("password", "reset_needed").Update(user) res, err := global.DB.NewUpdate().Model(user).WherePK().Column("password", "reset_needed").Exec(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
if affected > 0 { if affected, _ := res.RowsAffected(); affected > 0 {
// ! 同一个用户在缓存中有两个键 // ! 清除与此用户所有相关的记录
cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) cache.AbolishRelation(fmt.Sprintf("user:%s", uid))
cache.AbolishRelation("user")
return verifyCode, nil return verifyCode, nil
} else { } else {
return "", exceptions.NewUnsuccessfulOperationError() return "", exceptions.NewUnsuccessfulOperationError()
@@ -141,15 +149,16 @@ func (u _UserService) ResetUserPassword(username, password string) (bool, error)
if user == nil || err != nil { if user == nil || err != nil {
return false, exceptions.NewNotFoundError("指定的用户不存在。") return false, exceptions.NewNotFoundError("指定的用户不存在。")
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
user.Password = utils.Sha512Hex(password) user.Password = utils.Sha512Hex(password)
user.ResetNeeded = false user.ResetNeeded = false
affected, err := global.DBConn.ID(user.Id).Cols("password", "reset_needed").Update(user) res, err := global.DB.NewUpdate().Model(user).WherePK().Column("password", "reset_needed").Exec(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
if affected > 0 { if affected, _ := res.RowsAffected(); affected > 0 {
cache.AbolishRelation(fmt.Sprintf("user_%s", user.Id)) cache.AbolishRelation(fmt.Sprintf("user:%s", user.Id))
cache.AbolishRelation("user")
return true, nil return true, nil
} else { } else {
return false, nil return false, nil
@@ -160,9 +169,11 @@ func (_UserService) IsUserExists(uid string) (bool, error) {
if has, _ := cache.CheckExists("user", uid); has { if has, _ := cache.CheckExists("user", uid); has {
return has, nil return has, nil
} }
has, err := global.DBConn.ID(uid).Exist(&model.User{}) ctx, cancel := global.TimeoutContext()
defer cancel()
has, err := global.DB.NewSelect().Model((*model.User)(nil)).Where("id = ?", uid).Exists(ctx)
if has { if has {
cache.CacheExists([]string{fmt.Sprintf("user_%s", uid)}, "user", uid) cache.CacheExists([]string{"user", fmt.Sprintf("user_%s", uid)}, "user", uid)
} }
return has, err return has, err
} }
@@ -171,7 +182,9 @@ func (_UserService) IsUsernameExists(username string) (bool, error) {
if has, _ := cache.CheckExists("user", username); has { if has, _ := cache.CheckExists("user", username); has {
return has, nil return has, nil
} }
has, err := global.DBConn.Where(builder.Eq{"username": username}).Exist(&model.User{}) ctx, cancel := global.TimeoutContext()
defer cancel()
has, err := global.DB.NewSelect().Model((*model.User)(nil)).Where("username = ?", username).Exists(ctx)
if has { if has {
cache.CacheExists([]string{"user"}, "user", username) cache.CacheExists([]string{"user"}, "user", username)
} }
@@ -199,18 +212,19 @@ func (u _UserService) CreateUser(user *model.User, detail *model.UserDetail) (st
finalAbbr := tools.PinyinAbbr(*detail.Name) finalAbbr := tools.PinyinAbbr(*detail.Name)
detail.Abbr = &finalAbbr detail.Abbr = &finalAbbr
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
tx := global.DBConn.NewSession() tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
defer tx.Close() if err != nil {
if err := tx.Begin(); err != nil {
return "", err return "", err
} }
_, err = tx.Insert(user) _, err = tx.NewInsert().Model(user).Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return "", fmt.Errorf("user create failed: %w", err) return "", fmt.Errorf("user create failed: %w", err)
} }
_, err = tx.Insert(detail) _, err = tx.NewInsert().Model(detail).Exec(ctx)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return "", fmt.Errorf("user Detail create failed: %w", err) return "", fmt.Errorf("user Detail create failed: %w", err)
@@ -220,6 +234,7 @@ func (u _UserService) CreateUser(user *model.User, detail *model.UserDetail) (st
tx.Rollback() tx.Rollback()
return "", fmt.Errorf("transaction commit unsuccessful: %w", err) return "", fmt.Errorf("transaction commit unsuccessful: %w", err)
} }
// ! 广谱关联关系的废除必须是在有新记录加入或者有记录被删除的情况下。
cache.AbolishRelation("user") cache.AbolishRelation("user")
return verifyCode, nil return verifyCode, nil
} }
@@ -233,73 +248,103 @@ func (u _UserService) SwitchUserState(uid string, enabled bool) error {
return err return err
} }
newStateUser := new(model.User) newStateUser := new(model.User)
newStateUser.Id = uid
newStateUser.Enabled = enabled newStateUser.Enabled = enabled
_, err = global.DBConn.ID(uid).Cols("enabled").Update(newStateUser) ctx, cancel := global.TimeoutContext()
if err != nil { defer cancel()
cache.AbolishRelation("user") res, err := global.DB.NewUpdate().Model(newStateUser).WherePK().Column("enabled").Exec(ctx)
cache.AbolishRelation(fmt.Sprintf("user_%s", uid)) if affected, _ := res.RowsAffected(); err == nil && affected > 0 {
cache.AbolishRelation(fmt.Sprintf("user:%s", uid))
} }
return err return err
} }
func (_UserService) SearchLimitUsers(keyword string, limit int) ([]model.JoinedUserDetail, error) { func (us _UserService) SearchLimitUsers(keyword string, limit int) ([]model.JoinedUserDetail, error) {
if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", keyword, strconv.Itoa(limit)); cachedUsers != nil { if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", keyword, strconv.Itoa(limit)); cachedUsers != nil {
return *cachedUsers, nil return *cachedUsers, nil
} }
var users = make([]model.JoinedUserDetail, 0)
err := global.DBConn. ctx, cancel := global.TimeoutContext()
Table("user_detail").Alias("d"). defer cancel()
Join("INNER", []string{"user", "u"}, "d.id=u.id").
Where( var users = make([]model.User, 0)
builder.NewCond(). keywordCond := "%" + keyword + "%"
Or(builder.Like{"u.username", keyword}). err := global.DB.NewSelect().Model(&users).Relation("Detail").
Or(builder.Like{"d.name", keyword}). Where("u.type = ?", model.USER_TYPE_ENT).
Or(builder.Like{"d.abbr", keyword})). WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
And(builder.Eq{"u.type": 0}). return q.Where("u.username like ?", keywordCond).
Asc("u.created_at"). WhereOr("detail.name like ?", keywordCond).
Limit(limit, 0). WhereOr("detail.abbr like ?", keywordCond).
Find(&users) WhereOr("detail.contact like ?", keywordCond).
WhereOr("detail.address like ?", keywordCond)
}).
Order("u.created_at asc").
Limit(limit).
Offset(0).
Scan(ctx)
if err != nil { if err != nil {
return make([]model.JoinedUserDetail, 0), err return make([]model.JoinedUserDetail, 0), err
} }
cache.CacheSearch(users, []string{"user"}, "join_user_detail", keyword, strconv.Itoa(limit)) var detailedUsers = make([]model.JoinedUserDetail, 0)
return users, nil var relations = make([]string, 0)
// ! 这里的转换是为了兼容之前使用Xorm时构建的关联关系而存在的
for _, u := range users {
detailedUsers = append(detailedUsers, model.JoinedUserDetail{
UserDetail: *u.Detail,
Id: u.Id,
Username: u.Username,
Type: u.Type,
Enabled: u.Enabled,
})
relations = append(relations, fmt.Sprintf("user:%s", u.Id))
}
relations = append(relations, "user")
cache.CacheSearch(detailedUsers, relations, "join_user_detail", keyword, strconv.Itoa(limit))
return detailedUsers, nil
} }
func (_UserService) findUserWithCredentialsByUsername(username string) (*model.UserWithCredentials, error) { func (_UserService) findUserWithCredentialsByUsername(username string) (*model.UserWithCredentials, error) {
if cachedUser, _ := cache.RetreiveSearch[model.UserWithCredentials]("user_with_credentials", username); cachedUser != nil { if cachedUser, _ := cache.RetreiveSearch[model.UserWithCredentials]("user_with_credentials", username); cachedUser != nil {
return cachedUser, nil return cachedUser, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
user := new(model.UserWithCredentials) user := new(model.UserWithCredentials)
has, err := global.DBConn.Where(builder.Eq{"username": username}).NoAutoCondition().Get(user) err := global.DB.NewSelect().Model(user).Where("username = ?", username).Scan(ctx)
if has { if err == nil {
cache.CacheSearch(*user, []string{"user"}, "user_with_credentials", username) cache.CacheSearch(*user, []string{fmt.Sprintf("user:%s", user.Id)}, "user_with_credentials", username)
} }
return _postProcessSingle(user, has, err) return user, err
} }
func (_UserService) findUserByUsername(username string) (*model.User, error) { func (u _UserService) findUserByUsername(username string) (*model.User, error) {
if cachedUser, _ := cache.RetreiveSearch[model.User]("user", username); cachedUser != nil { if cachedUser, _ := cache.RetreiveSearch[model.User]("user", username); cachedUser != nil {
return cachedUser, nil return cachedUser, nil
} }
ctx, cancel := global.TimeoutContext()
defer cancel()
user := new(model.User) user := new(model.User)
has, err := global.DBConn.Where(builder.Eq{"username": username}).NoAutoCondition().Get(user) err := global.DB.NewSelect().Model(user).Where("username = ?", username).Scan(ctx)
if has { if err == nil {
cache.CacheSearch(*user, []string{"user"}, "user", username) cache.CacheSearch(*user, []string{fmt.Sprintf("user:%s", user.Id)}, "user", username)
} }
return _postProcessSingle(user, has, err) return user, err
} }
func (_UserService) retreiveUserDetail(uid string) (*model.UserDetail, error) { func (_UserService) retreiveUserDetail(uid string) (*model.UserDetail, error) {
if cachedUser, _ := cache.RetreiveEntity[model.UserDetail]("user_detail", uid); cachedUser != nil { if cachedUser, _ := cache.RetreiveEntity[model.UserDetail]("user_detail", uid); cachedUser != nil {
return cachedUser, nil return cachedUser, nil
} }
user := new(model.UserDetail) ctx, cancel := global.TimeoutContext()
has, err := global.DBConn.ID(uid).NoAutoCondition().Get(user) defer cancel()
if has { user := &model.UserDetail{
cache.CacheEntity(*user, []string{fmt.Sprintf("user_%s", uid)}, "user_detail", uid) Id: uid,
} }
return _postProcessSingle(user, has, err) err := global.DB.NewSelect().Model(user).WherePK().Scan(ctx)
if err == nil {
cache.CacheEntity(*user, []string{fmt.Sprintf("user:%s", uid)}, "user_detail", uid)
}
return user, err
} }
func (_UserService) findUserByID(uid string) (*model.User, error) { func (_UserService) findUserByID(uid string) (*model.User, error) {
@@ -307,82 +352,96 @@ func (_UserService) findUserByID(uid string) (*model.User, error) {
if cachedUser != nil { if cachedUser != nil {
return cachedUser, nil return cachedUser, nil
} }
user := new(model.User) ctx, cancel := global.TimeoutContext()
has, err := global.DBConn.ID(uid).NoAutoCondition().Get(user) defer cancel()
if has { user := &model.User{
cache.CacheEntity(*user, []string{fmt.Sprintf("user_%s", uid)}, "user", uid) Id: uid,
} }
return _postProcessSingle(user, has, err) err := global.DB.NewSelect().Model(&user).WherePK().Scan(ctx)
if err == nil {
cache.CacheEntity(*user, []string{fmt.Sprintf("user:%s", uid)}, "user", uid)
}
return user, err
} }
func (_UserService) ListUserDetail(keyword string, userType int, userState *bool, page int) ([]model.JoinedUserDetail, int64, error) { func (_UserService) ListUserDetail(keyword string, userType int, userState *bool, page int) ([]model.JoinedUserDetail, int64, error) {
var ( var (
cond = builder.NewCond() cond = global.DB.NewSelect()
cacheConditions = make([]string, 0) cacheConditions = make([]string, 0)
users = make([]model.User, 0)
) )
cond = cond.Model(&users).Relation("Detail")
cacheConditions = append(cacheConditions, strconv.Itoa(page)) cacheConditions = append(cacheConditions, strconv.Itoa(page))
cond = cond.And(builder.Neq{"d.id": "000"}) cond = cond.Where("detail.id <> ?", "000")
if len(keyword) != 0 { if len(keyword) != 0 {
keywordCond := builder.NewCond(). keywordCond := "%" + keyword + "%"
Or(builder.Like{"u.username", keyword}). cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
Or(builder.Like{"d.name", keyword}) return q.Where("u.username like ?", keywordCond).
cond = cond.And(keywordCond) WhereOr("detail.name like ?", keywordCond)
})
cacheConditions = append(cacheConditions, keyword) cacheConditions = append(cacheConditions, keyword)
} }
if userType != -1 { if userType != -1 {
cond = cond.And(builder.Eq{"u.type": userType}) cond = cond.Where("u.type = ?", userType)
cacheConditions = append(cacheConditions, strconv.Itoa(userType)) cacheConditions = append(cacheConditions, strconv.Itoa(userType))
} }
if userState != nil { if userState != nil {
cond = cond.And(builder.Eq{"u.enabled": *userState}) cond = cond.Where("u.enabled = ?", *userState)
cacheConditions = append(cacheConditions, strconv.FormatBool(*userState)) cacheConditions = append(cacheConditions, strconv.FormatBool(*userState))
} }
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
var (
total int64 // * 这里利用已经构建完成的条件集合从缓存中获取数据,如果所有数据都可以从缓存中获取,那么就直接返回了。
err error if cacheCounts, err := cache.RetreiveCount("join_user_detail", cacheConditions...); cacheCounts != -1 && err == nil {
)
if cacheCounts, _ := cache.RetreiveCount("join_user_detail", cacheConditions...); cacheCounts != -1 {
total = cacheCounts
} else {
total, err = global.DBConn.
Table("user_detail").Alias("d").
Join("INNER", []string{"user", "u"}, "d.id=u.id").
Where(cond).
Count(&model.User{})
if err != nil {
return nil, -1, err
}
cache.CacheCount([]string{"user"}, "join_user_detail", total, cacheConditions...)
}
users := make([]model.JoinedUserDetail, 0)
if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", cacheConditions...); cachedUsers != nil { if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", cacheConditions...); cachedUsers != nil {
return *cachedUsers, total, nil return *cachedUsers, cacheCounts, nil
} }
err = global.DBConn. }
Table("user_detail").Alias("d").
Join("INNER", []string{"user", "u"}, "d.id=u.id"). ctx, cancel := global.TimeoutContext()
Where(cond). defer cancel()
Limit(config.ServiceSettings.ItemsPageSize, startItem). total, err := cond.
Find(&users) Limit(config.ServiceSettings.ItemsPageSize).Offset(startItem).
cache.CacheSearch(users, []string{"user"}, "join_user_detail", cacheConditions...) ScanAndCount(ctx)
return users, total, err
var (
joinedUsers = make([]model.JoinedUserDetail, 0)
relations = []string{"user"}
)
for _, u := range users {
joinedUsers = append(joinedUsers, model.JoinedUserDetail{
UserDetail: *u.Detail,
Id: u.Id,
Username: u.Username,
Type: u.Type,
Enabled: u.Enabled,
})
relations = append(relations, fmt.Sprintf("user:%s", u.Id))
}
cache.CacheCount(relations, "join_user_detail", int64(total), cacheConditions...)
cache.CacheSearch(joinedUsers, relations, "join_user_detail", cacheConditions...)
return joinedUsers, int64(total), err
} }
func (_UserService) FetchUserDetail(uid string) (*model.FullJoinedUserDetail, error) { func (_UserService) FetchUserDetail(uid string) (*model.FullJoinedUserDetail, error) {
if cachedUser, _ := cache.RetreiveEntity[model.FullJoinedUserDetail]("full_join_user_detail", uid); cachedUser != nil { if cachedUser, _ := cache.RetreiveEntity[model.FullJoinedUserDetail]("full_join_user_detail", uid); cachedUser != nil {
return cachedUser, nil return cachedUser, nil
} }
user := &model.FullJoinedUserDetail{}
has, err := global.DBConn. ctx, cancel := global.TimeoutContext()
Table("user_detail").Alias("d"). defer cancel()
Join("INNER", []string{"user", "u"}, "d.id=u.id"). user := &model.User{}
Where(builder.Eq{"d.id": uid}). err := global.DB.NewSelect().Model(user).Relation("Detail").
NoAutoCondition(). Where("u.id = ?", uid).
Get(user) Scan(ctx)
if has { if err == nil {
cache.CacheEntity(*user, []string{fmt.Sprintf("user_%s", uid)}, "full_join_user_detail", uid) fullJoinedUser := &model.FullJoinedUserDetail{
return user, nil User: *user,
UserDetail: *user.Detail,
}
cache.CacheEntity(*fullJoinedUser, []string{fmt.Sprintf("user:%s", uid)}, "full_join_user_detail", uid)
return fullJoinedUser, nil
} }
return nil, err return nil, err
} }

View File

@@ -5,146 +5,156 @@ import (
"electricity_bill_calc/config" "electricity_bill_calc/config"
"electricity_bill_calc/exceptions" "electricity_bill_calc/exceptions"
"electricity_bill_calc/global" "electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model" "electricity_bill_calc/model"
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
"github.com/samber/lo" "github.com/samber/lo"
"xorm.io/builder" "github.com/uptrace/bun"
"go.uber.org/zap"
) )
type _WithdrawService struct{} type _WithdrawService struct {
l *zap.Logger
}
var WithdrawService _WithdrawService var WithdrawService = _WithdrawService{
l: logger.Named("Service", "Withdraw"),
}
func (_WithdrawService) ApplyWithdraw(reportId string) (bool, error) { func (_WithdrawService) ApplyWithdraw(reportId string) (bool, error) {
ctx, cancel := global.TimeoutContext()
defer cancel()
var report = new(model.Report) var report = new(model.Report)
has, err := global.DBConn.ID(reportId).Get(report) err := global.DB.NewSelect().Model(report).
if err != nil { Where("id = ?", reportId).
return false, err Scan(ctx)
} if err != nil || report == nil {
if !has { return false, exceptions.NewNotFoundErrorFromError("指定报表未能找到", err)
return false, exceptions.NewNotFoundError("指定报表未能找到")
} }
if !report.Published { if !report.Published {
return false, exceptions.NewImproperOperateError("指定报表尚未发布。") return false, exceptions.NewImproperOperateError("指定报表尚未发布。")
} }
reports := make([]model.Report, 0) var maxPublished time.Time
err = global.DBConn. err = global.DB.NewSelect().Model((*model.Report)(nil)).
Where(builder.Eq{"park_id": report.ParkId}). ColumnExpr("max(period)").
Find(&reports) Where("park_id = ?", report.ParkId).
Where("published = ?", true).
Scan(ctx, &maxPublished)
if err != nil { if err != nil {
return false, exceptions.NewNotFoundError("未能找到匹配的系列报表。") return false, exceptions.NewNotFoundError("未能找到匹配的系列报表。")
} }
maxPublished := lo.Reduce( if !report.Period.Equal(maxPublished) {
reports,
func(acc *time.Time, elem model.Report, index int) *time.Time {
if elem.Published {
if acc == nil || (acc != nil && elem.Period.After(*acc)) {
return &elem.Period
}
}
return acc
},
nil,
)
if !report.Period.Equal(*maxPublished) {
return false, exceptions.NewImproperOperateError("申请撤回的报表必须是最新已发布的报表。") return false, exceptions.NewImproperOperateError("申请撤回的报表必须是最新已发布的报表。")
} }
report.Withdraw = model.REPORT_WITHDRAW_APPLIED report.Withdraw = model.REPORT_WITHDRAW_APPLIED
report.LastWithdrawAppliedAt = lo.ToPtr(time.Now()) report.LastWithdrawAppliedAt = lo.ToPtr(time.Now())
_, err = global.DBConn.ID(report.Id).Cols("withdraw", "last_withdraw_applied_at").Update(report) _, err = global.DB.NewUpdate().Model(report).
WherePK().
Column("withdraw", "last_withdraw_applied_at").
Exec(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
cache.AbolishRelation("report") cache.AbolishRelation("withdraw_stat")
cache.AbolishRelation(fmt.Sprintf("publicity_%s", reportId)) cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId))
return true, nil return true, nil
} }
func (_WithdrawService) FetchPagedWithdrawApplies(page int, keyword string) ([]model.JoinedReportForWithdraw, int64, error) { func (_WithdrawService) FetchPagedWithdrawApplies(page int, keyword string) ([]model.JoinedReportForWithdraw, int64, error) {
var conditions = make([]string, 0) var (
cond := builder.NewCond() conditions = make([]string, 0)
conditions = append(conditions, strconv.Itoa(int(model.REPORT_WITHDRAW_APPLIED)), strconv.Itoa(page)) reports = make([]model.Report, 0)
cond = cond.And(builder.Eq{"r.withdraw": model.REPORT_WITHDRAW_APPLIED}) cond = global.DB.NewSelect().Model(&reports).
if len(keyword) > 0 { Relation("Park").Relation("Park.Enterprise")
cond = cond.And(
builder.Like{"p.name", keyword}.
Or(
builder.Like{"p.abbr", keyword},
builder.Like{"u.name", keyword},
builder.Like{"u.abbr", keyword},
),
) )
conditions = append(conditions, strconv.Itoa(int(model.REPORT_WITHDRAW_APPLIED)), strconv.Itoa(page))
cond = cond.Where("r.withdraw = ?", model.REPORT_WITHDRAW_APPLIED)
if len(keyword) > 0 {
keywordCond := "%" + keyword + "%"
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
return q.Where("p.name like ?", keywordCond).
WhereOr("p.abbr like ?", keywordCond).
WhereOr("d.name like ?", keywordCond).
WhereOr("d.abbr like ?", keywordCond)
})
conditions = append(conditions, keyword) conditions = append(conditions, keyword)
} }
var reports = make([]model.JoinedReportForWithdraw, 0) if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil {
var (
total int64
err error
)
if cachedTotal, _ := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 {
total = cachedTotal
} else {
total, err = global.DBConn.
Table(new(model.JoinedReportForWithdraw)).Alias("r").
Join("INNER", []string{"park", "p"}, "r.park_id=p.id").
Join("INNER", []string{"user_detail", "u"}, "p.user_id=u.id").
Where(cond).
Count()
if err != nil {
return nil, -1, err
}
cache.CacheCount([]string{"report", "park"}, "join_report_for_withdraw", total, conditions...)
}
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
if cachedReports, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_user_detail", conditions...); cachedReports != nil { if cachedReports, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_user_detail", conditions...); cachedReports != nil {
return *cachedReports, total, err return *cachedReports, cachedTotal, err
} }
err = global.DBConn. }
Alias("r").
Join("INNER", []string{"park", "p"}, "r.park_id=p.id"). ctx, cancel := global.TimeoutContext()
Join("INNER", []string{"user_detail", "u"}, "p.user_id=u.id"). defer cancel()
Where(cond). startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
Limit(config.ServiceSettings.ItemsPageSize, startItem). total, err := cond.Limit(config.ServiceSettings.ItemsPageSize).
Find(&reports) Offset(startItem).
cache.CacheSearch(reports, []string{"report", "park"}, "join_report_for_withdraw", conditions...) ScanAndCount(ctx)
return reports, total, err
var (
joinedReports = make([]model.JoinedReportForWithdraw, 0)
relations = []string{"report", "park"}
)
for _, r := range reports {
joinedReports = append(joinedReports, model.JoinedReportForWithdraw{
Report: r,
Park: model.FromPark(*r.Park),
User: model.FromUserDetail(*r.Park.Enterprise),
})
relations = append(relations, fmt.Sprintf("report:%s", r.Id), fmt.Sprintf("publicity:%s", r.Id))
}
cache.CacheCount(relations, "join_report_for_withdraw", int64(total), conditions...)
cache.CacheSearch(joinedReports, relations, "join_report_for_withdraw", conditions...)
return joinedReports, int64(total), err
} }
func (_WithdrawService) AuditWithdraw(reportId string, granted bool) error { func (_WithdrawService) AuditWithdraw(reportId string, granted bool) error {
ctx, cancel := global.TimeoutContext()
defer cancel()
var report = new(model.Report) var report = new(model.Report)
has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(report) err := global.DB.NewSelect().Model(report).
Where("id = ?", reportId).
Scan(ctx)
if err != nil { if err != nil {
return err return exceptions.NewNotFoundErrorFromError("指定公示报表未找到。", err)
}
if !has {
return exceptions.NewNotFoundError("指定公示报表未找到。")
} }
report.Withdraw = lo.If(granted, model.REPORT_WITHDRAW_GRANTED).Else(model.REPORT_WITHDRAW_DENIED) report.Withdraw = lo.If(granted, model.REPORT_WITHDRAW_GRANTED).Else(model.REPORT_WITHDRAW_DENIED)
report.LastWithdrawAuditAt = lo.ToPtr(time.Now()) report.LastWithdrawAuditAt = lo.ToPtr(time.Now())
if granted { if granted {
report.Published = false report.Published = false
} }
_, err = global.DBConn.ID(report.Id).Cols("withdraw", "last_withdraw_audit_at", "published").Update(report) _, err = global.DB.NewUpdate().Model(report).
cache.AbolishRelation("report") WherePK().
Column("withdraw", "last_withdraw_audit_at", "published").
Exec(ctx)
if err == nil {
cache.AbolishRelation("withdraw_stat")
cache.AbolishRelation(fmt.Sprintf("report:%s", reportId))
}
return err return err
} }
func (_WithdrawService) AuditWaits() (int64, error) { func (_WithdrawService) AuditWaits() (int64, error) {
if cachedWaits, _ := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 { if cachedWaits, err := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 && err == nil {
return cachedWaits, nil return cachedWaits, nil
} }
cond := builder.NewCond() ctx, cancel := global.TimeoutContext()
cond = cond.And(builder.Eq{"withdraw": model.REPORT_WITHDRAW_APPLIED}) defer cancel()
total, err := global.DBConn.
Table(new(model.JoinedReportForWithdraw)). total, err := global.DB.NewSelect().Model((*model.Report)(nil)).
Where(cond). Where("withdraw = ?", model.REPORT_WITHDRAW_APPLIED).
Count() Count(ctx)
if err == nil { if err == nil {
cache.CacheCount([]string{"report", "park"}, "withdraw_waits", total) cache.CacheCount([]string{"withdraw_stat"}, "withdraw_waits", int64(total))
} }
return total, err return int64(total), err
} }

View File

@@ -4,8 +4,8 @@ Database:
Host: postgres Host: postgres
Port: 5432 Port: 5432
DB: electricity DB: electricity
MaxIdleConns: 10 MaxIdleConns: 0
MaxOpenConns: 30 MaxOpenConns: 20
Server: Server:
RunMode: release RunMode: release
HttpPort: 8000 HttpPort: 8000

View File

@@ -38,3 +38,16 @@ func ConvertSliceToInterfaceSlice[T any](origin []T) []interface{} {
} }
return dest return dest
} }
// 对指定的数组进行分区
func PartitionSlice[T any](slice []T, chunkSize int) [][]T {
var divided [][]T
for i := 0; i < len(slice); i += int(chunkSize) {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
divided = append(divided, slice[i:end])
}
return divided
}