forked from free-lancers/electricity_bill_calc_service
		
	enhance(calculate): 完善计算部分
This commit is contained in:
		
							
								
								
									
										9
									
								
								.idea/0.2.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.idea/0.2.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="Go" enabled="true" /> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$" /> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										28
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <component name="ProjectCodeStyleConfiguration"> | ||||
|   <code_scheme name="Project" version="173"> | ||||
|     <DBN-PSQL> | ||||
|       <case-options enabled="true"> | ||||
|         <option name="KEYWORD_CASE" value="lower" /> | ||||
|         <option name="FUNCTION_CASE" value="lower" /> | ||||
|         <option name="PARAMETER_CASE" value="lower" /> | ||||
|         <option name="DATATYPE_CASE" value="lower" /> | ||||
|         <option name="OBJECT_CASE" value="preserve" /> | ||||
|       </case-options> | ||||
|       <formatting-settings enabled="false" /> | ||||
|     </DBN-PSQL> | ||||
|     <DBN-SQL> | ||||
|       <case-options enabled="true"> | ||||
|         <option name="KEYWORD_CASE" value="lower" /> | ||||
|         <option name="FUNCTION_CASE" value="lower" /> | ||||
|         <option name="PARAMETER_CASE" value="lower" /> | ||||
|         <option name="DATATYPE_CASE" value="lower" /> | ||||
|         <option name="OBJECT_CASE" value="preserve" /> | ||||
|       </case-options> | ||||
|       <formatting-settings enabled="false"> | ||||
|         <option name="STATEMENT_SPACING" value="one_line" /> | ||||
|         <option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" /> | ||||
|         <option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" /> | ||||
|       </formatting-settings> | ||||
|     </DBN-SQL> | ||||
|   </code_scheme> | ||||
| </component> | ||||
							
								
								
									
										11
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="DataSourceManagerImpl" format="xml" multifile-model="true"> | ||||
|     <data-source source="LOCAL" name="PostgreSQL - postgres@39.105.39.8" uuid="996b1b9f-5c40-4bd6-8c3c-0af67aaaa15d"> | ||||
|       <driver-ref>postgresql</driver-ref> | ||||
|       <synchronize>true</synchronize> | ||||
|       <jdbc-driver>org.postgresql.Driver</jdbc-driver> | ||||
|       <jdbc-url>jdbc:postgresql://39.105.39.8:9432/postgres</jdbc-url> | ||||
|     </data-source> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										8
									
								
								.idea/electricity_bill_calc_service.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/electricity_bill_calc_service.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$" /> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="JavaScriptSettings"> | ||||
|     <option name="languageLevel" value="ES6" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/0.2.iml" filepath="$PROJECT_DIR$/.idea/0.2.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
										
											Binary file not shown.
										
									
								
							| @@ -36,12 +36,18 @@ type ServiceSetting struct { | ||||
| 	HostSerial     int64 | ||||
| } | ||||
|  | ||||
| // 读取基准线损率 | ||||
| type BaseLossSetting struct { | ||||
| 	Base string | ||||
| } | ||||
|  | ||||
| // 定义全局变量 | ||||
| var ( | ||||
| 	ServerSettings   *ServerSetting | ||||
| 	DatabaseSettings *DatabaseSetting | ||||
| 	RedisSettings    *RedisSetting | ||||
| 	ServiceSettings  *ServiceSetting | ||||
| 	BaseLoss         *BaseLossSetting | ||||
| ) | ||||
|  | ||||
| // 读取配置到全局变量 | ||||
| @@ -69,5 +75,10 @@ func SetupSetting() error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = s.ReadSection("BaselineLineLossRatio", &BaseLoss) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -1,237 +0,0 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"electricity_bill_calc/excel" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/service" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| func InitializeEndUserController(router *fiber.App) { | ||||
| 	router.Get("/report/:rid/submeter", security.EnterpriseAuthorize, fetchEndUserInReport) | ||||
| 	router.Get("/report/:rid/meter/template", downloadEndUserRegisterTemplate) | ||||
| 	router.Post("/report/:rid/meter/batch", security.EnterpriseAuthorize, uploadEndUserRegisterTemplate) | ||||
| 	router.Put("/report/:rid/submeter/:pid/:mid", security.EnterpriseAuthorize, modifyEndUserRegisterRecord) | ||||
| 	router.Get("/end/user/adjusts", security.MustAuthenticated, statEndUserInPeriod) | ||||
| } | ||||
|  | ||||
| func fetchEndUserInReport(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	keyword := c.Query("keyword") | ||||
| 	requestPage, err := strconv.Atoi(c.Query("page", "1")) | ||||
| 	if err != nil { | ||||
| 		return result.NotAccept("查询参数[page]格式不正确。") | ||||
| 	} | ||||
| 	endUsers, totalItem, err := service.EndUserService.SearchEndUserRecord(requestReportId, keyword, requestPage) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	return result.Json( | ||||
| 		http.StatusOK, | ||||
| 		"已获取到符合条件的终端用户集合", | ||||
| 		response.NewPagedResponse(requestPage, totalItem).ToMap(), | ||||
| 		fiber.Map{"meters": endUsers}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func downloadEndUserRegisterTemplate(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	users, err := service.EndUserService.AllEndUserRecord(requestReportId) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	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) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	if meterType == -1 { | ||||
| 		return result.NotFound("未能确定用户表计类型。") | ||||
| 	} | ||||
|  | ||||
| 	c.Status(http.StatusOK) | ||||
| 	c.Set("Content-Type", "application/octet-stream") | ||||
| 	c.Set("Content-Transfer-Encoding", "binary") | ||||
| 	c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=抄表记录-%s-%s.xlsx", park.Name, reportIndex.Period.Format("2006-01"))) | ||||
|  | ||||
| 	gen := lo.Ternary[excel.ExcelTemplateGenerator]( | ||||
| 		meterType == 0, | ||||
| 		excel.NewMeterNonPVExcelTemplateGenerator(), | ||||
| 		excel.NewMeterPVExcelTemplateGenerator(), | ||||
| 	) | ||||
| 	defer gen.Close() | ||||
| 	gen.WriteMeterData(users) | ||||
| 	gen.WriteTo(c.Response().BodyWriter()) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func uploadEndUserRegisterTemplate(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	if meterType == -1 { | ||||
| 		return result.NotFound("未能确定用户表计类型。") | ||||
| 	} | ||||
|  | ||||
| 	uploadedFile, err := c.FormFile("data") | ||||
| 	if err != nil { | ||||
| 		return result.NotAccept("没有接收到上传的档案文件。") | ||||
| 	} | ||||
| 	archiveFile, err := uploadedFile.Open() | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	if meterType == 0 { | ||||
| 		errs := service.EndUserService.BatchImportNonPVRegister(requestReportId, archiveFile) | ||||
| 		if errs.Len() > 0 { | ||||
| 			return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs}) | ||||
| 		} | ||||
| 	} else { | ||||
| 		errs := service.EndUserService.BatchImportPVRegister(requestReportId, archiveFile) | ||||
| 		if errs.Len() > 0 { | ||||
| 			return result.Json(http.StatusInternalServerError, "上传抄表文件存在解析错误", fiber.Map{"errors": errs.Errs}) | ||||
| 		} | ||||
| 	} | ||||
| 	return result.Json(http.StatusOK, "已经成功完成抄表记录的导入。", fiber.Map{"errors": make([]error, 0)}) | ||||
| } | ||||
|  | ||||
| type ModifyEndUserRegisterFormData struct { | ||||
| 	CurrentPeriodOverall  decimal.NullDecimal `json:"currentPeriodOverall" form:"currentPeriodOverall"` | ||||
| 	CurrentPeriodCritical decimal.NullDecimal `json:"currentPeriodCritical" form:"currentPeriodCritical"` | ||||
| 	CurrentPeriodPeak     decimal.NullDecimal `json:"currentPeriodPeak" form:"currentPeriodPeak"` | ||||
| 	CurrentPeriodValley   decimal.NullDecimal `json:"currentPeriodValley" form:"currentPeriodValley"` | ||||
| 	AdjustOverall         decimal.NullDecimal `json:"adjustOverall" form:"adjustOverall"` | ||||
| 	AdjustCritical        decimal.NullDecimal `json:"adjustCritical" form:"adjustCritical"` | ||||
| 	AdjustPeak            decimal.NullDecimal `json:"adjustPeak" form:"adjustPeak"` | ||||
| 	AdjustValley          decimal.NullDecimal `json:"adjustValley" form:"adjustValley"` | ||||
| } | ||||
|  | ||||
| func modifyEndUserRegisterRecord(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	meterType, err := service.ReportService.RetreiveParkEndUserMeterType(requestReportId) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	if meterType == -1 { | ||||
| 		return result.NotFound("未能确定用户表计类型。") | ||||
| 	} | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	requestMeterId := c.Params("mid") | ||||
| 	formData := new(ModifyEndUserRegisterFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	meter, err := service.EndUserService.FetchSpecificEndUserRecord(requestReportId, requestParkId, requestMeterId) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	if formData.CurrentPeriodOverall.Valid { | ||||
| 		meter.CurrentPeriodOverall = formData.CurrentPeriodOverall.Decimal | ||||
| 	} | ||||
| 	if formData.CurrentPeriodCritical.Valid { | ||||
| 		meter.CurrentPeriodCritical = formData.CurrentPeriodCritical.Decimal | ||||
| 	} | ||||
| 	if formData.CurrentPeriodPeak.Valid { | ||||
| 		meter.CurrentPeriodPeak = formData.CurrentPeriodPeak.Decimal | ||||
| 	} | ||||
| 	if formData.CurrentPeriodValley.Valid { | ||||
| 		meter.CurrentPeriodValley = formData.CurrentPeriodValley.Decimal | ||||
| 	} | ||||
| 	if formData.AdjustOverall.Valid { | ||||
| 		meter.AdjustOverall = formData.AdjustOverall.Decimal | ||||
| 	} | ||||
| 	if formData.AdjustCritical.Valid { | ||||
| 		meter.AdjustCritical = formData.AdjustCritical.Decimal | ||||
| 	} | ||||
| 	if formData.AdjustPeak.Valid { | ||||
| 		meter.AdjustPeak = formData.AdjustPeak.Decimal | ||||
| 	} | ||||
| 	if formData.AdjustValley.Valid { | ||||
| 		meter.AdjustValley = formData.AdjustValley.Decimal | ||||
| 	} | ||||
| 	valid, err := meter.Validate() | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	if !valid { | ||||
| 		return result.NotAccept("抄表数据合法性验证失败。") | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	err = service.EndUserService.UpdateEndUserRegisterRecord(&tx, &ctx, *meter) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	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}, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										24
									
								
								controller/foundation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								controller/foundation.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| var foundLog = logger.Named("Handler", "Foundation") | ||||
|  | ||||
| func InitializeFoundationHandlers(router *fiber.App) { | ||||
| 	router.Get("/norm/authorized/loss/rate", security.MustAuthenticated, getNormAuthorizedLossRate) | ||||
| } | ||||
|  | ||||
| func getNormAuthorizedLossRate(c *fiber.Ctx) error { | ||||
| 	foundLog.Info("获取系统中定义的基准核定线损率") | ||||
| 	result := response.NewResult(c) | ||||
| 	BaseLoss := config.BaseLoss | ||||
| 	return result.Success("已经获取到系统设置的基准核定线损率。", | ||||
| 		fiber.Map{"normAuthorizedLossRate": BaseLoss}, | ||||
| 	) | ||||
| } | ||||
| @@ -1,176 +1,132 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/service" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"errors" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"go.uber.org/zap" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| 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) | ||||
| 	} | ||||
| var GmLog = logger.Named("Handler", "GM") | ||||
|  | ||||
| func InitializeGmController(router *fiber.App) { | ||||
| 	router.Delete("/gm/tenement", security.SingularityAuthorize, deleteTenement) | ||||
| 	router.Delete("/gm/park", security.SingularityAuthorize, deletePark) | ||||
| 	router.Delete("/gm/report", security.SingularityAuthorize, deleteReports) | ||||
| 	router.Delete("/gm/tenement/meter", security.SingularityAuthorize, deleteTenementMeterRelations) | ||||
| 	router.Delete("/gm/enterprise", security.SingularityAuthorize, deleteEnterprise) | ||||
| 	router.Delete("/gm/meter/pooling", security.SingularityAuthorize, deleteMeterPoolingRelations) | ||||
| 	router.Delete("gm/meter", security.SingularityAuthorize, deleteMeters) | ||||
| } | ||||
|  | ||||
| func 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()) | ||||
| //用于将参数转化为切片 | ||||
| func getQueryValues(c *fiber.Ctx, paramName string) []string { | ||||
| 	values := c.Request().URI().QueryArgs().PeekMulti(paramName) | ||||
| 	result := make([]string, len(values)) | ||||
| 	for i, v := range values { | ||||
| 		result[i] = string(v) | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的园区总览部分。") | ||||
| 	} | ||||
| 	return result.Success("指定报表的园区总览已经重置。") | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func gmResetReportMaintenance(c *fiber.Ctx) error { | ||||
| func deleteTenement(c *fiber.Ctx) error { | ||||
| 	park := c.Query("park", "") | ||||
| 	tenements := getQueryValues(c, "tenements") | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	done, err := service.GodModeService.ClearReportMaintenances(requestReportId) | ||||
| 	GmLog.Info("[天神模式]删除指定园区中的商户", zap.String("park", park), zap.Strings("tenements", tenements)) | ||||
|  | ||||
| 	err := service.GMService.DeleteTenements(park, tenements) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		GmLog.Error("[天神模式]删除指定园区中的商户失败", zap.Error(err)) | ||||
| 		return result.Error(500, err.Error()) | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的配电维护费部分。") | ||||
| 	} | ||||
| 	return result.Success("指定报表的配电维护费已经重置。") | ||||
|  | ||||
| 	return result.Success("指定商户已经删除。") | ||||
| } | ||||
|  | ||||
| func gmResynchronizeReportEndUserRecord(c *fiber.Ctx) error { | ||||
| func deletePark(c *fiber.Ctx) error { | ||||
| 	parks := getQueryValues(c, "parks") | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	done, err := service.GodModeService.ResynchronizeEndUser(requestReportId) | ||||
| 	GmLog.Info("[天神模式]删除指定园区", zap.Strings("parks", parks)) | ||||
|  | ||||
| 	if len(parks) < 0 { | ||||
| 		GmLog.Info("[天神模式]用户未指派园区参数或者未指定需要删除的园区。") | ||||
| 		return result.Error(http.StatusBadRequest, error.Error(errors.New("必须至少指定一个需要删除的园区!"))) | ||||
| 	} | ||||
|  | ||||
| 	err := service.GMService.DeleteParks(parks) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		GmLog.Error("[天神模式]删除指定园区失败", zap.Error(err)) | ||||
| 		return result.Error(500, err.Error()) | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录基本档案。") | ||||
| 	} | ||||
| 	return result.Success("指定报表的抄表记录基本档案已经重新同步。") | ||||
| 	return result.Success("指定园区已经删除。") | ||||
| } | ||||
|  | ||||
| func gmResetReportEndUserRecord(c *fiber.Ctx) error { | ||||
| func deleteReports(c *fiber.Ctx) error { | ||||
| 	pid := c.Query("park") | ||||
| 	reports := getQueryValues(c, "reports") | ||||
| 	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("指定报表的抄表记录已经重置。") | ||||
| } | ||||
| 	GmLog.Info("[天神模式]删除符合条件的报表。", zap.Strings("reports", reports)) | ||||
|  | ||||
| func gmResetReport(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestReportId := c.Params("rid") | ||||
| 	done, err := service.GodModeService.ResetReport(requestReportId) | ||||
| 	err := service.GMService.DeleteReports(pid, reports) | ||||
| 	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, "未能成功删除指定报表。") | ||||
| 		GmLog.Error("[天神模式]删除指定园区中的报表失败。", zap.Error(err)) | ||||
| 		return result.Error(500, err.Error()) | ||||
| 	} | ||||
| 	return result.Success("指定报表已经删除。") | ||||
| } | ||||
|  | ||||
| func gmDeleteSpecificMaintenance(c *fiber.Ctx) error { | ||||
| func deleteEnterprise(c *fiber.Ctx) error { | ||||
| 	uid := c.Query("uid") | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	requestMaintenanceId := c.Params("mid") | ||||
| 	done, err := service.GodModeService.RemoveSpecificMaintenance(requestParkId, requestMaintenanceId) | ||||
| 	GmLog.Info("[天神模式]删除指定企业用户", zap.String("uid", uid)) | ||||
|  | ||||
| 	err := service.GMService.DeleteEnterprises(uid) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		GmLog.Error("[天神模式]删除指定企业用户失败", zap.Error(err)) | ||||
| 		return result.Error(500, "删除指定企业用户失败。") | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功删除指定的维护费用记录。") | ||||
| 	} | ||||
| 	return result.Success("指定维护费用记录已经删除。") | ||||
| 	return result.Success("指定企业用户已经删除。") | ||||
| } | ||||
|  | ||||
| func gmDeleteAllMaintenance(c *fiber.Ctx) error { | ||||
| func deleteTenementMeterRelations(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()) | ||||
| 	parkId := c.Query("park") | ||||
| 	tId := getQueryValues(c, "tenements") | ||||
| 	metersId := getQueryValues(c, "meters") | ||||
| 	GmLog.Info("删除指定园区中的商户与表计的关联关系", zap.String("park id", parkId)) | ||||
| 	if err := service.GMService.DeleteTenementMeterRelations(parkId, tId, metersId); err != nil { | ||||
| 		meterLog.Error("无法删除指定园区中的商户与表计的关联关系", zap.Error(err)) | ||||
| 		return result.NotAccept(err.Error()) | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功删除全部维护费用记录。") | ||||
| 	} | ||||
| 	return result.Success("全部维护费用记录已经删除。") | ||||
| 	return result.Success("删除成功") | ||||
| } | ||||
|  | ||||
| func gmDeleteAllMeters(c *fiber.Ctx) error { | ||||
| func deleteMeterPoolingRelations(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()) | ||||
| 	parkId := c.Query("park") | ||||
| 	mId := getQueryValues(c, "meters") | ||||
| 	GmLog.Info("[天神模式]删除指定园区中的表计公摊关系", zap.String("park id", parkId)) | ||||
| 	if err := service.GMService.DeleteMeterPooling(parkId, mId); err != nil { | ||||
| 		meterLog.Error("[天神模式]删除指定园区中的表计公摊关系失败", zap.Error(err)) | ||||
| 		return result.Error(500, "删除指定园区中的表计公摊关系失败。") | ||||
| 	} | ||||
| 	if !done { | ||||
| 		return result.Error(http.StatusInternalServerError, "未能成功删除全部终端表计档案记录。") | ||||
| 	} | ||||
| 	return result.Success("全部终端表计档案记录已经删除。") | ||||
| 	return result.Success("指定表计公摊关系已经删除。") | ||||
|  | ||||
| } | ||||
|  | ||||
| func gmDeletePark(c *fiber.Ctx) error { | ||||
| func deleteMeters(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()) | ||||
| 	parkId := c.Query("park") | ||||
| 	mId := getQueryValues(c, "meters") | ||||
| 	GmLog.Info("[天神模式]删除指定园区中的表计", zap.String("park id", parkId)) | ||||
| 	if err := service.GMService.DeleteMeters(parkId, mId); err != nil { | ||||
| 		meterLog.Error("[天神模式]删除指定园区中的表计失败", zap.Error(err)) | ||||
| 		return result.Error(500, "删除指定园区中的表计失败。") | ||||
| 	} | ||||
| 	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("指定的用户及其关联信息已经删除。") | ||||
| 	return result.Success("指定表计已经删除。") | ||||
| } | ||||
|   | ||||
| @@ -1,202 +0,0 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/service" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/jinzhu/copier" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| func InitializeMaintenanceFeeController(router *fiber.App) { | ||||
| 	router.Get("/maintenance/fee", security.MustAuthenticated, listMaintenanceFees) | ||||
| 	router.Post("/maintenance/fee", security.EnterpriseAuthorize, createMaintenanceFeeRecord) | ||||
| 	router.Put("/maintenance/fee/:mid", security.EnterpriseAuthorize, modifyMaintenanceFeeRecord) | ||||
| 	router.Put("/maintenance/fee/:mid/enabled", security.EnterpriseAuthorize, changeMaintenanceFeeState) | ||||
| 	router.Delete("/maintenance/fee/:mid", security.EnterpriseAuthorize, deleteMaintenanceFee) | ||||
| 	router.Get("/additional/charges", security.MustAuthenticated, statAdditionalCharges) | ||||
| } | ||||
|  | ||||
| func ensureMaintenanceFeeBelongs(c *fiber.Ctx, result *response.Result, requestMaintenanceFeeId string) (bool, error) { | ||||
| 	userSession, err := _retreiveSession(c) | ||||
| 	if err != nil { | ||||
| 		return false, result.Unauthorized(err.Error()) | ||||
| 	} | ||||
| 	sure, err := service.MaintenanceFeeService.EnsureFeeBelongs(userSession.Uid, requestMaintenanceFeeId) | ||||
| 	if err != nil { | ||||
| 		return false, result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	if !sure { | ||||
| 		return false, result.Unauthorized("所操作维护费记录不属于当前用户。") | ||||
| 	} | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| func listMaintenanceFees(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	userSession, err := _retreiveSession(c) | ||||
| 	if err != nil { | ||||
| 		return result.Unauthorized(err.Error()) | ||||
| 	} | ||||
| 	requestPark := c.Query("park") | ||||
| 	requestPeriod := c.Query("period") | ||||
| 	requestPage, err := strconv.Atoi(c.Query("page", "1")) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, "不能解析给定的参数[page]。") | ||||
| 	} | ||||
| 	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 { | ||||
| 		parkIds, err := service.ParkService.AllParkIds(userSession.Uid) | ||||
| 		if err != nil { | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 		fees, total, err := service.MaintenanceFeeService.ListMaintenanceFees(parkIds, 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}, | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type _FeeCreationFormData struct { | ||||
| 	ParkId string          `json:"parkId" form:"parkId"` | ||||
| 	Name   string          `json:"name" form:"name"` | ||||
| 	Period string          `json:"period" form:"period"` | ||||
| 	Fee    decimal.Decimal `json:"fee" form:"fee"` | ||||
| 	Memo   *string         `json:"memo" form:"memo"` | ||||
| } | ||||
|  | ||||
| func createMaintenanceFeeRecord(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	formData := new(_FeeCreationFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, formData.ParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	newMaintenanceFee := &model.MaintenanceFee{} | ||||
| 	copier.Copy(newMaintenanceFee, formData) | ||||
| 	err := service.MaintenanceFeeService.CreateMaintenanceFeeRecord(*newMaintenanceFee) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Created("新维护费记录已经创建。") | ||||
| } | ||||
|  | ||||
| type _FeeModificationFormData struct { | ||||
| 	Fee  decimal.Decimal `json:"fee" form:"fee"` | ||||
| 	Memo *string         `json:"memo" form:"memo"` | ||||
| } | ||||
|  | ||||
| func modifyMaintenanceFeeRecord(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestFee := c.Params("mid") | ||||
| 	formData := new(_FeeModificationFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	newFeeState := new(model.MaintenanceFee) | ||||
| 	copier.Copy(newFeeState, formData) | ||||
| 	newFeeState.Id = requestFee | ||||
| 	err := service.MaintenanceFeeService.ModifyMaintenanceFee(*newFeeState) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Updated("指定维护费条目已更新。") | ||||
| } | ||||
|  | ||||
| type _FeeStateFormData struct { | ||||
| 	Enabled bool `json:"enabled" form:"enabled"` | ||||
| } | ||||
|  | ||||
| func changeMaintenanceFeeState(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestFee := c.Params("mid") | ||||
| 	formData := new(_FeeStateFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	err := service.MaintenanceFeeService.ChangeMaintenanceFeeState(requestFee, formData.Enabled) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Updated("指定维护费条目状态已更新。") | ||||
| } | ||||
|  | ||||
| func deleteMaintenanceFee(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestFee := c.Params("mid") | ||||
| 	if ensure, err := ensureMaintenanceFeeBelongs(c, &result, requestFee); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	err := service.MaintenanceFeeService.DeleteMaintenanceFee(requestFee) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	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}, | ||||
| 	) | ||||
| } | ||||
| @@ -1,188 +0,0 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/excel" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/service" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/jinzhu/copier" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| func InitializeMeter04kVController(router *fiber.App) { | ||||
| 	router.Get("/park/:pid/meter/template", download04kvMeterArchiveTemplate) | ||||
| 	router.Get("/park/:pid/meters", security.EnterpriseAuthorize, ListPaged04kVMeter) | ||||
| 	router.Get("/park/:pid/meter/:code", security.EnterpriseAuthorize, fetch04kVMeterDetail) | ||||
| 	router.Post("/park/:pid/meter", security.EnterpriseAuthorize, createSingle04kVMeter) | ||||
| 	router.Post("/park/:pid/meter/batch", security.EnterpriseAuthorize, batchImport04kVMeterArchive) | ||||
| 	router.Put("/park/:pid/meter/:code", security.EnterpriseAuthorize, modifySingle04kVMeter) | ||||
| } | ||||
|  | ||||
| func download04kvMeterArchiveTemplate(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	parkDetail, err := service.ParkService.FetchParkDetail(requestParkId) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound("未找到指定的园区信息。") | ||||
| 	} | ||||
| 	return c.Download("./assets/meter_04kv_template.xlsx", fmt.Sprintf("%s-户表档案.xlsx", parkDetail.Name)) | ||||
| } | ||||
|  | ||||
| func ListPaged04kVMeter(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	requestPage, err := strconv.Atoi(c.Query("page", "1")) | ||||
| 	if err != nil { | ||||
| 		return result.NotAccept("查询参数[page]格式不正确。") | ||||
| 	} | ||||
| 	requestKeyword := c.Query("keyword", "") | ||||
| 	meters, totalItem, err := service.Meter04kVService.ListMeterDetail(requestParkId, requestKeyword, requestPage) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	return result.Json( | ||||
| 		http.StatusOK, | ||||
| 		"已获取到符合条件的0.4kV表计集合。", | ||||
| 		response.NewPagedResponse(requestPage, totalItem).ToMap(), | ||||
| 		fiber.Map{"meters": meters}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func fetch04kVMeterDetail(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	requestMeterCode := c.Params("code") | ||||
| 	meter, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	if meter == nil { | ||||
| 		return result.Json(http.StatusNotFound, "指定的表计信息未能找到。", fiber.Map{"meter": nil}) | ||||
| 	} | ||||
| 	return result.Json(http.StatusOK, "指定的表计信息已找到。", fiber.Map{"meter": meter}) | ||||
| } | ||||
|  | ||||
| type _MeterModificationFormData struct { | ||||
| 	Address       *string         `json:"address" form:"address"` | ||||
| 	CustomerName  *string         `json:"customerName" form:"customerName"` | ||||
| 	ContactName   *string         `json:"contactName" form:"contactName"` | ||||
| 	ContactPhone  *string         `json:"contactPhone" form:"contactPhone"` | ||||
| 	Ratio         decimal.Decimal `json:"ratio" form:"ratio"` | ||||
| 	Seq           int             `json:"seq" form:"seq"` | ||||
| 	IsPublicMeter bool            `json:"isPublicMeter" form:"isPublicMeter"` | ||||
| 	Enabled       bool            `json:"enabled" form:"enabled"` | ||||
| } | ||||
|  | ||||
| type _MeterCreationFormData struct { | ||||
| 	Code          string          `json:"code" form:"code"` | ||||
| 	Address       *string         `json:"address" form:"address"` | ||||
| 	CustomerName  *string         `json:"customerName" form:"customerName"` | ||||
| 	ContactName   *string         `json:"contactName" form:"contactName"` | ||||
| 	ContactPhone  *string         `json:"contactPhone" form:"contactPhone"` | ||||
| 	Ratio         decimal.Decimal `json:"ratio" form:"ratio"` | ||||
| 	Seq           int             `json:"seq" form:"seq"` | ||||
| 	IsPublicMeter bool            `json:"isPublicMeter" form:"isPublicMeter"` | ||||
| 	Enabled       bool            `json:"enabled" form:"enabled"` | ||||
| } | ||||
|  | ||||
| func createSingle04kVMeter(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	formData := new(_MeterCreationFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	newMeter := new(model.Meter04KV) | ||||
| 	copier.Copy(newMeter, formData) | ||||
| 	newMeter.ParkId = requestParkId | ||||
| 	err := service.Meter04kVService.CreateSingleMeter(*newMeter) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Created("新0.4kV表计已经添加完成。") | ||||
| } | ||||
|  | ||||
| func modifySingle04kVMeter(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	requestMeterCode := c.Params("code") | ||||
| 	meterDetail, err := service.Meter04kVService.Get04kVMeterDetail(requestParkId, requestMeterCode) | ||||
| 	if err != nil { | ||||
| 		return result.NotFound(err.Error()) | ||||
| 	} | ||||
| 	if meterDetail == nil { | ||||
| 		return result.NotFound("指定表计的信息为找到,不能修改。") | ||||
| 	} | ||||
| 	formData := new(_MeterModificationFormData) | ||||
| 	if err := c.BodyParser(formData); err != nil { | ||||
| 		return result.UnableToParse("无法解析提交的数据。") | ||||
| 	} | ||||
| 	copier.Copy(meterDetail, formData) | ||||
| 	err = service.Meter04kVService.UpdateSingleMeter(meterDetail) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Updated("指定0.4kV表计信息已经更新。") | ||||
| } | ||||
|  | ||||
| func batchImport04kVMeterArchive(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	requestParkId := c.Params("pid") | ||||
| 	if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { | ||||
| 		return err | ||||
| 	} | ||||
| 	uploadedFile, err := c.FormFile("data") | ||||
| 	if err != nil { | ||||
| 		return result.NotAccept("没有接收到上传的档案文件。") | ||||
| 	} | ||||
| 	archiveFile, err := uploadedFile.Open() | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	analyzer, err := excel.NewMeterArchiveExcelAnalyzer(archiveFile) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	records, errs := analyzer.Analysis(*new(model.Meter04KV)) | ||||
| 	if len(errs) > 0 { | ||||
| 		return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs}) | ||||
| 	} | ||||
|  | ||||
| 	mergedMeters := lo.Map(records, func(meter model.Meter04KV, index int) model.Meter04KV { | ||||
| 		meter.ParkId = requestParkId | ||||
| 		meter.Enabled = true | ||||
| 		return meter | ||||
| 	}) | ||||
| 	errs = service.Meter04kVService.DuplicateMeterCodeValidate(mergedMeters) | ||||
| 	if len(errs) > 0 { | ||||
| 		return result.Json(http.StatusNotAcceptable, "上传的表计档案文件存在错误。", fiber.Map{"errors": errs}) | ||||
| 	} | ||||
| 	err = service.Meter04kVService.BatchCreateMeter(mergedMeters) | ||||
| 	if err != nil { | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Json( | ||||
| 		http.StatusOK, | ||||
| 		"上传的表计档案已经全部导入。", | ||||
| 		fiber.Map{"errors": make([]excel.ExcelAnalysisError, 0)}, | ||||
| 	) | ||||
| } | ||||
| @@ -24,6 +24,8 @@ func InitializeReportHandlers(router *fiber.App) { | ||||
| 	router.Post("/report", security.EnterpriseAuthorize, initNewReportCalculateTask) | ||||
| 	router.Get("/report/draft", security.EnterpriseAuthorize, listDraftReportIndicies) | ||||
| 	router.Post("/report/calcualte", security.EnterpriseAuthorize, testCalculateReportSummary) | ||||
| 	//TODO: 2023-07-20将calcualte错误请求改为正确的calculate请求 | ||||
| 	router.Post("/report/calculate", security.EnterpriseAuthorize, testCalculateReportSummary) | ||||
| 	router.Get("/report/calculate/status", security.EnterpriseAuthorize, listCalculateTaskStatus) | ||||
| 	router.Get("/report/:rid", security.EnterpriseAuthorize, getReportDetail) | ||||
| 	router.Put("/report/:rid", security.EnterpriseAuthorize, updateReportCalculateTask) | ||||
|   | ||||
| @@ -1,31 +1,36 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/service" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"go.uber.org/zap" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| var StatisticsWithdrawLog = logger.Named("Handler", "StatisticsWithdraw") | ||||
|  | ||||
| func InitializeStatisticsController(router *fiber.App) { | ||||
| 	router.Get("/audits", security.OPSAuthorize, currentAuditAmount) | ||||
| 	router.Get("/stat/reports", security.MustAuthenticated, statReports) | ||||
| 	router.Get("/stat/reports", security.OPSAuthorize, statReports) | ||||
|  | ||||
| } | ||||
|  | ||||
| //获取当前系统中待审核的内容数量 | ||||
| func currentAuditAmount(c *fiber.Ctx) error { | ||||
| 	StatisticsWithdrawLog.Info("开始获取当前系统中待审核的内容数量") | ||||
| 	result := response.NewResult(c) | ||||
| 	amount, err := service.WithdrawService.AuditWaits() | ||||
| 	if err != nil { | ||||
| 		StatisticsWithdrawLog.Error("获取当前系统中待审核的内容数量出错", zap.Error(err)) | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return result.Json(http.StatusOK, "已经获取到指定的统计信息。", fiber.Map{ | ||||
| 		"amounts": map[string]int64{ | ||||
| 			"withdraw": amount, | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| 	return result.Success("已经获取到指定的统计信息", | ||||
| 		fiber.Map{"withdraw": amount}) | ||||
| } | ||||
|  | ||||
| func statReports(c *fiber.Ctx) error { | ||||
| @@ -42,28 +47,35 @@ func statReports(c *fiber.Ctx) error { | ||||
| 	if session.Type != 0 { | ||||
| 		enterprises, err = service.StatisticsService.EnabledEnterprises() | ||||
| 		if err != nil { | ||||
| 			StatisticsWithdrawLog.Error(err.Error()) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 		parks, err = service.StatisticsService.EnabledParks() | ||||
| 		if err != nil { | ||||
| 			StatisticsWithdrawLog.Error(err.Error()) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 		reports, err = service.StatisticsService.ParksNewestState() | ||||
| 		//TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 | ||||
| 		reports, err = service.StatisticsService.ParkNewestState() | ||||
| 		if err != nil { | ||||
| 			StatisticsWithdrawLog.Error(err.Error()) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		parks, err = service.StatisticsService.EnabledParks(session.Uid) | ||||
| 		if err != nil { | ||||
| 			StatisticsWithdrawLog.Error(err.Error()) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 		reports, err = service.StatisticsService.ParksNewestState(session.Uid) | ||||
| 		//TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 | ||||
| 		reports, err = service.StatisticsService.ParkNewestState(session.Uid) | ||||
| 		if err != nil { | ||||
| 			StatisticsWithdrawLog.Error(err.Error()) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result.Json(http.StatusOK, "已经完成园区报告的统计。", fiber.Map{ | ||||
| 	return result.Success("已经完成园区报告的统计。", fiber.Map{ | ||||
| 		"statistics": fiber.Map{ | ||||
| 			"enterprises": enterprises, | ||||
| 			"parks":       parks, | ||||
|   | ||||
| @@ -28,6 +28,11 @@ func InitializeTenementHandler(router *fiber.App) { | ||||
| 	router.Post("/tenement/:pid/:tid/move/out", security.EnterpriseAuthorize, moveOutTenement) | ||||
| 	router.Post("/tenement/:pid", security.EnterpriseAuthorize, addTenement) | ||||
| 	router.Post("/tenement/:pid/:tid/binding", security.EnterpriseAuthorize, bindMeterToTenement) | ||||
| 	//TODO: 2023-07-19再apiFox上该请求是个PUT请求,后端接收是个POST请求,不知道是否有误或是缺少对应请求(apiFox测试请求返回值为405) | ||||
| 	router.Post("/tenement/:pid/:tid/move/out", security.EnterpriseAuthorize, moveOutTenement) | ||||
| 	router.Post("/tenement/:pid", security.EnterpriseAuthorize, addTenement) | ||||
| 	router.Post("/tenement/:pid/:tid/binding", security.EnterpriseAuthorize, bindMeterToTenement) | ||||
| 	//TODO: 2023-07-19再apiFox上该请求是个PUT请求,后端接收是个POST请求,不知道是否有误或是缺少对应请求(apiFox测试请求返回值为405) | ||||
| 	router.Post("/tenement/:pid/:tid/binding/:code/unbind", security.EnterpriseAuthorize, unbindMeterFromTenement) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -18,8 +18,6 @@ import ( | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| var userLog = logger.Named("Handler", "User") | ||||
|  | ||||
| func InitializeUserHandlers(router *fiber.App) { | ||||
| 	router.Delete("/login", security.MustAuthenticated, doLogout) | ||||
| 	router.Post("/login", doLogin) | ||||
| @@ -35,6 +33,8 @@ func InitializeUserHandlers(router *fiber.App) { | ||||
| 	router.Delete("/password/:uid", security.OPSAuthorize, invalidUserPassword) | ||||
| } | ||||
|  | ||||
| var userLog = logger.Named("Handler", "User") | ||||
|  | ||||
| type _LoginForm struct { | ||||
| 	Username string `json:"uname"` | ||||
| 	Password string `json:"upass"` | ||||
| @@ -71,7 +71,6 @@ func doLogin(c *fiber.Ctx) error { | ||||
| 	} | ||||
| 	return result.LoginSuccess(session) | ||||
| } | ||||
|  | ||||
| func doLogout(c *fiber.Ctx) error { | ||||
| 	result := response.NewResult(c) | ||||
| 	session, err := _retreiveSession(c) | ||||
|   | ||||
| @@ -2,18 +2,134 @@ package controller | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/logger" | ||||
|  | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"electricity_bill_calc/response" | ||||
| 	"electricity_bill_calc/security" | ||||
| 	"electricity_bill_calc/vo" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"go.uber.org/zap" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| var withdrawLog = logger.Named("Handler", "Report") | ||||
| var withdrawLog = logger.Named("Handler", "Withdraw") | ||||
|  | ||||
| func InitializewithdrawHandlers(router *fiber.App) { | ||||
| 	router.Put("/withdraw/:rid", security.EnterpriseAuthorize, changeReportWithdraw) | ||||
| func InitializeWithdrawHandlers(router *fiber.App) { | ||||
| 	router.Get("/withdraw", security.OPSAuthorize, withdraw) | ||||
| 	router.Put("/withdraw/:rid", security.OPSAuthorize, reviewRequestWithdraw) | ||||
| 	router.Delete("/withdraw/:rid", security.EnterpriseAuthorize, recallReport) | ||||
| } | ||||
| func changeReportWithdraw(ctx *fiber.Ctx) error { | ||||
| 	//result := response.NewResult(ctx) | ||||
| 	//reportId := ctx.Params("rid") | ||||
| 	return nil | ||||
|  | ||||
| // 用于分页检索用户的核算报表 | ||||
| func withdraw(c *fiber.Ctx) error { | ||||
| 	//记录日志 | ||||
| 	withdrawLog.Info("带分页的待审核的核算撤回申请列表") | ||||
| 	//获取请求参数 | ||||
| 	result := response.NewResult(c) | ||||
| 	keyword := c.Query("keyword", "") | ||||
| 	page := c.QueryInt("page", 1) | ||||
| 	withdrawLog.Info("参数为: ", zap.String("keyword", keyword), zap.Int("page", page)) | ||||
| 	//中间数据库操作暂且省略。。。。 | ||||
| 	//首先进行核算报表的分页查询 | ||||
| 	withdraws, total, err := repository.WithdrawRepository.FindWithdraw(uint(page), &keyword) | ||||
| 	if err != nil { | ||||
| 		withdrawLog.Error("检索用户核算报表失败。", zap.Error(err)) | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	//TODO: 2023-07-18 此处返回值是个示例,具体返回值需要查询数据库(完成) | ||||
| 	return result.Success( | ||||
| 		"withdraw请求成功", | ||||
| 		response.NewPagedResponse(page, total).ToMap(), | ||||
| 		fiber.Map{"records": withdraws}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| // 用于审核撤回报表 | ||||
| func reviewRequestWithdraw(c *fiber.Ctx) error { | ||||
| 	Rid := c.Params("rid", "") | ||||
| 	Data := new(vo.ReviewWithdraw) | ||||
| 	result := response.NewResult(c) | ||||
|  | ||||
| 	if err := c.BodyParser(&Data); err != nil { | ||||
| 		withdrawLog.Error("无法解析审核指定报表的请求数据", zap.Error(err)) | ||||
| 		return result.BadRequest("无法解析审核指定报表的请求数据。") | ||||
| 	} | ||||
|  | ||||
| 	if Data.Audit == true { //审核通过 | ||||
| 		ok, err := repository.WithdrawRepository.ReviewTrueReportWithdraw(Rid) | ||||
| 		if err != nil { | ||||
| 			withdrawLog.Error("审核同意撤回报表失败") | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
|  | ||||
| 		if !ok { | ||||
| 			withdrawLog.Error("审核同意撤回报表失败") | ||||
| 			return result.NotAccept("审核同意撤回报表失败") | ||||
| 		} else { | ||||
| 			return result.Success("审核同意撤回报表成功!") | ||||
| 		} | ||||
| 	} else { //审核不通过 | ||||
| 		ok, err := repository.WithdrawRepository.ReviewFalseReportWithdraw(Rid) | ||||
| 		if err != nil { | ||||
| 			withdrawLog.Error("审核拒绝撤回报表失败") | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
|  | ||||
| 		if !ok { | ||||
| 			withdrawLog.Error("审核拒绝撤回报表失败") | ||||
| 			return result.NotAccept("审核拒绝撤回报表失败") | ||||
| 		} else { | ||||
| 			return result.Success("审核拒绝撤回报表成功!") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // 用于撤回电费核算 | ||||
| func recallReport(c *fiber.Ctx) error { | ||||
| 	//	获取用户会话信息和参数 | ||||
| 	rid := c.Params("rid", "") | ||||
| 	result := response.NewResult(c) | ||||
| 	session, err := _retreiveSession(c) | ||||
| 	if err != nil { | ||||
| 		withdrawLog.Error("无法获取当前用户的会话。") | ||||
| 		return result.Unauthorized(err.Error()) | ||||
| 	} | ||||
| 	//	检查指定报表的所属情况 | ||||
| 	isBelongsTo, err := repository.ReportRepository.IsBelongsTo(rid, session.Uid) | ||||
| 	if err != nil { | ||||
| 		withdrawLog.Error("检查报表所属情况出现错误。", zap.Error(err)) | ||||
| 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	if err == nil && isBelongsTo == true { | ||||
| 		//	判断指定报表是否是当前园区的最后一张报表 | ||||
| 		isLastReport, err := repository.ReportRepository.IsLastReport(rid) | ||||
| 		if err != nil { | ||||
| 			withdrawLog.Error("判断指定报表是否为当前园区的最后一张报表时出错", zap.Error(err)) | ||||
| 			return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 		} | ||||
|  | ||||
| 		if err == nil && isLastReport == true { | ||||
| 			//	申请撤回指定的核算报表 | ||||
| 			//TODO: 2023.07.25 申请撤回指定核算报表,正确状态未处理(完成) | ||||
| 			ok, err := repository.ReportRepository.ApplyWithdrawReport(rid) | ||||
| 			if err != nil { | ||||
| 				withdrawLog.Error("申请撤回指定核算报表出错", zap.Error(err)) | ||||
| 				return result.Error(http.StatusInternalServerError, err.Error()) | ||||
| 			} | ||||
| 			if ok { | ||||
| 				withdrawLog.Info("申请撤回指定核算报表成功") | ||||
| 				return result.Success("申请撤回指定核算报表成功") | ||||
| 			} | ||||
| 		} else { | ||||
| 			withdrawLog.Info("当前报表不是当前园区的最后一张报表") | ||||
| 			return result.Error(http.StatusForbidden, "当前报表不是当前园区的最后一张报表") | ||||
| 		} | ||||
| 	} else { | ||||
| 		withdrawLog.Info("指定的核算报表不属于当前用户。") | ||||
| 		return result.Error(http.StatusForbidden, "指定的核算报表不属于当前用户") | ||||
| 	} | ||||
|  | ||||
| 	return result.Error(http.StatusInternalServerError, "其他错误") | ||||
| } | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| package excel | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	endUserNonPVRecognizers = []*ColumnRecognizer{ | ||||
| 		{Pattern: []string{"电表编号"}, Tag: "meterId", MatchIndex: -1, MustFill: true}, | ||||
| 		{Pattern: []string{"上期", "(总)"}, Tag: "lastPeriodOverall", MatchIndex: -1, MustFill: true}, | ||||
| 		{Pattern: []string{"本期", "(总)"}, Tag: "currentPeriodOverall", MatchIndex: -1, MustFill: true}, | ||||
| 		{Pattern: []string{"退补", "(总)"}, Tag: "adjustOverall", MatchIndex: -1, MustFill: true}, | ||||
| 	} | ||||
| 	endUserPVRecognizers = append( | ||||
| 		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: "currentPeriodPeak", MatchIndex: -1}, | ||||
| 		&ColumnRecognizer{Pattern: []string{"本期", "(谷)"}, Tag: "currentPeriodValley", MatchIndex: -1}, | ||||
| 		&ColumnRecognizer{Pattern: []string{"退补", "(尖峰)"}, Tag: "adjustCritical", MatchIndex: -1}, | ||||
| 		&ColumnRecognizer{Pattern: []string{"退补", "(峰)"}, Tag: "adjustPeak", MatchIndex: -1}, | ||||
| 		&ColumnRecognizer{Pattern: []string{"退补", "(谷)"}, Tag: "adjustValley", MatchIndex: -1}, | ||||
| 	) | ||||
| ) | ||||
|  | ||||
| func NewEndUserNonPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) { | ||||
| 	return NewExcelAnalyzer[model.EndUserImport](file, endUserNonPVRecognizers) | ||||
| } | ||||
|  | ||||
| func NewEndUserPVExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.EndUserImport], error) { | ||||
| 	return NewExcelAnalyzer[model.EndUserImport](file, endUserPVRecognizers) | ||||
| } | ||||
| @@ -1,90 +0,0 @@ | ||||
| package excel | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/xuri/excelize/v2" | ||||
| ) | ||||
|  | ||||
| type MeterNonPVExcelTemplateGenerator struct { | ||||
| 	file *excelize.File | ||||
| } | ||||
|  | ||||
| // 生成峰谷计量抄表Excel模板 | ||||
| func NewMeterNonPVExcelTemplateGenerator() *MeterNonPVExcelTemplateGenerator { | ||||
| 	return &MeterNonPVExcelTemplateGenerator{ | ||||
| 		file: excelize.NewFile(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (MeterNonPVExcelTemplateGenerator) titles() []interface{} { | ||||
| 	return []interface{}{ | ||||
| 		"序号", | ||||
| 		"用户名称", | ||||
| 		"户址", | ||||
| 		"电表编号", | ||||
| 		"倍率", | ||||
| 		"上期表底(总)", | ||||
| 		"本期表底(总)", | ||||
| 		"退补电量(总)", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *MeterNonPVExcelTemplateGenerator) Close() { | ||||
| 	t.file.Close() | ||||
| } | ||||
|  | ||||
| func (t MeterNonPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) { | ||||
| 	return t.file.WriteTo(w) | ||||
| } | ||||
|  | ||||
| func (t *MeterNonPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error { | ||||
| 	defaultSheet := t.file.GetSheetName(0) | ||||
| 	stream, err := t.file.NewStreamWriter(defaultSheet) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	firstCell, err := excelize.CoordinatesToCellName(1, 1) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	stream.SetColWidth(2, 4, 20) | ||||
| 	stream.SetColWidth(6, 8, 15) | ||||
| 	stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20}) | ||||
|  | ||||
| 	for index, meter := range meters { | ||||
| 		firstCell, err := excelize.CoordinatesToCellName(1, index+2) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		customerName := "" | ||||
| 		if meter.CustomerName != nil { | ||||
| 			customerName = *meter.CustomerName | ||||
| 		} | ||||
| 		customerAddress := "" | ||||
| 		if meter.Address != nil { | ||||
| 			customerAddress = *meter.Address | ||||
| 		} | ||||
| 		if err = stream.SetRow( | ||||
| 			firstCell, | ||||
| 			[]interface{}{ | ||||
| 				meter.Seq, | ||||
| 				customerName, | ||||
| 				customerAddress, | ||||
| 				meter.MeterId, | ||||
| 				meter.Ratio, | ||||
| 				meter.LastPeriodOverall, | ||||
| 				meter.CurrentPeriodOverall, | ||||
| 				meter.AdjustOverall, | ||||
| 			}, | ||||
| 			excelize.RowOpts{Height: 15}, | ||||
| 		); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	if err = stream.Flush(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,108 +0,0 @@ | ||||
| package excel | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/xuri/excelize/v2" | ||||
| ) | ||||
|  | ||||
| type MeterPVExcelTemplateGenerator struct { | ||||
| 	file *excelize.File | ||||
| } | ||||
|  | ||||
| // 生成峰谷计量抄表Excel模板 | ||||
| func NewMeterPVExcelTemplateGenerator() *MeterPVExcelTemplateGenerator { | ||||
| 	return &MeterPVExcelTemplateGenerator{ | ||||
| 		file: excelize.NewFile(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (MeterPVExcelTemplateGenerator) titles() []interface{} { | ||||
| 	return []interface{}{ | ||||
| 		"序号", | ||||
| 		"用户名称", | ||||
| 		"户址", | ||||
| 		"电表编号", | ||||
| 		"倍率", | ||||
| 		"上期表底(总)", | ||||
| 		"本期表底(总)", | ||||
| 		"上期表底(尖峰)", | ||||
| 		"本期表底(尖峰)", | ||||
| 		"上期表底(峰)", | ||||
| 		"本期表底(峰)", | ||||
| 		"上期表底(谷)", | ||||
| 		"本期表底(谷)", | ||||
| 		"退补电量(总)", | ||||
| 		"退补电量(尖峰)", | ||||
| 		"退补电量(峰)", | ||||
| 		"退补电量(谷)", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *MeterPVExcelTemplateGenerator) Close() { | ||||
| 	t.file.Close() | ||||
| } | ||||
|  | ||||
| func (t MeterPVExcelTemplateGenerator) WriteTo(w io.Writer) (int64, error) { | ||||
| 	return t.file.WriteTo(w) | ||||
| } | ||||
|  | ||||
| func (t *MeterPVExcelTemplateGenerator) WriteMeterData(meters []model.EndUserDetail) error { | ||||
| 	defaultSheet := t.file.GetSheetName(0) | ||||
| 	stream, err := t.file.NewStreamWriter(defaultSheet) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	firstCell, err := excelize.CoordinatesToCellName(1, 1) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	stream.SetColWidth(2, 4, 20) | ||||
| 	stream.SetColWidth(6, 17, 15) | ||||
| 	stream.SetRow(firstCell, t.titles(), excelize.RowOpts{Height: 20}) | ||||
|  | ||||
| 	for index, meter := range meters { | ||||
| 		firstCell, err := excelize.CoordinatesToCellName(1, index+2) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		customerName := "" | ||||
| 		if meter.CustomerName != nil { | ||||
| 			customerName = *meter.CustomerName | ||||
| 		} | ||||
| 		customerAddress := "" | ||||
| 		if meter.Address != nil { | ||||
| 			customerAddress = *meter.Address | ||||
| 		} | ||||
| 		if err = stream.SetRow( | ||||
| 			firstCell, | ||||
| 			[]interface{}{ | ||||
| 				meter.Seq, | ||||
| 				customerName, | ||||
| 				customerAddress, | ||||
| 				meter.MeterId, | ||||
| 				meter.Ratio, | ||||
| 				meter.LastPeriodOverall, | ||||
| 				meter.CurrentPeriodOverall, | ||||
| 				meter.LastPeriodCritical, | ||||
| 				meter.CurrentPeriodCritical, | ||||
| 				meter.LastPeriodPeak, | ||||
| 				meter.CurrentPeriodPeak, | ||||
| 				meter.LastPeriodValley, | ||||
| 				meter.CurrentPeriodValley, | ||||
| 				meter.AdjustOverall, | ||||
| 				meter.AdjustCritical, | ||||
| 				meter.AdjustPeak, | ||||
| 				meter.AdjustValley, | ||||
| 			}, | ||||
| 			excelize.RowOpts{Height: 15}, | ||||
| 		); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	if err = stream.Flush(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,10 +1,5 @@ | ||||
| package excel | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| var tenementRecognizers = []*ColumnRecognizer{ | ||||
| 	{Pattern: [][]string{{"商户全称"}}, Tag: "fullName", MatchIndex: -1}, | ||||
| 	{Pattern: [][]string{{"联系地址"}}, Tag: "address", MatchIndex: -1}, | ||||
| @@ -18,9 +13,9 @@ var tenementRecognizers = []*ColumnRecognizer{ | ||||
| 	{Pattern: [][]string{{"开户行"}}, Tag: "bank", MatchIndex: -1}, | ||||
| } | ||||
|  | ||||
| func NewTenementExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.TenementImportRow], error) { | ||||
| 	return NewExcelAnalyzer[model.TenementImportRow](file, tenementRecognizers) | ||||
| } | ||||
| //func NewTenementExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.TenementImportRow], error) { | ||||
| //	return NewExcelAnalyzer[model.TenementImportRow](file, tenementRecognizers) | ||||
| //} | ||||
|  | ||||
| //func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.MeterImportRow], error) { | ||||
| //	return NewExcelAnalyzer[model.MeterImportRow](file, meterArchiveRecognizers) | ||||
|   | ||||
| @@ -53,6 +53,7 @@ func (ql QueryLogger) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data | ||||
| 	ql.logger.Info("查询参数", lo.Map(data.Args, func(elem any, index int) zap.Field { | ||||
| 		return zap.Any(fmt.Sprintf("[Arg %d]: ", index), elem) | ||||
| 	})...) | ||||
|  | ||||
| 	return ctx | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,8 @@ var ( | ||||
|  | ||||
| func SetupRedisConnection() error { | ||||
| 	var err error | ||||
| 	a := fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port) | ||||
| 	fmt.Println(a) | ||||
| 	Rd, err = rueidis.NewClient(rueidis.ClientOption{ | ||||
| 		InitAddress: []string{fmt.Sprintf("%s:%d", config.RedisSettings.Host, config.RedisSettings.Port)}, | ||||
| 		Password:    config.RedisSettings.Password, | ||||
|   | ||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,7 +3,6 @@ module electricity_bill_calc | ||||
| go 1.19 | ||||
|  | ||||
| require ( | ||||
| 	github.com/deckarep/golang-set/v2 v2.1.0 | ||||
| 	github.com/fufuok/utils v0.10.2 | ||||
| 	github.com/georgysavva/scany/v2 v2.0.0 | ||||
| 	github.com/gofiber/fiber/v2 v2.46.0 | ||||
| @@ -27,26 +26,18 @@ require ( | ||||
| 	github.com/jackc/pgpassfile v1.0.0 // indirect | ||||
| 	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect | ||||
| 	github.com/jackc/puddle/v2 v2.2.0 // indirect | ||||
| 	github.com/jinzhu/inflection v1.0.0 // indirect | ||||
| 	github.com/jmoiron/sqlx v1.3.5 // indirect | ||||
| 	github.com/klauspost/compress v1.16.6 // indirect | ||||
| 	github.com/mattn/go-colorable v0.1.13 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.19 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.14 // indirect | ||||
| 	github.com/philhofer/fwd v1.1.2 // indirect | ||||
| 	github.com/rivo/uniseg v0.4.4 // indirect | ||||
| 	github.com/rogpeppe/go-internal v1.9.0 // indirect | ||||
| 	github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect | ||||
| 	github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect | ||||
| 	github.com/tinylib/msgp v1.1.8 // indirect | ||||
| 	github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect | ||||
| 	github.com/valyala/bytebufferpool v1.0.0 // indirect | ||||
| 	github.com/valyala/tcplisten v1.0.0 // indirect | ||||
| 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect | ||||
| 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect | ||||
| 	golang.org/x/sync v0.2.0 // indirect | ||||
| 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||||
| 	mellium.im/sasl v0.3.0 // indirect | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| @@ -59,7 +50,6 @@ require ( | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect | ||||
| 	github.com/pelletier/go-toml v1.9.5 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.0.8 // indirect | ||||
| 	github.com/richardlehane/mscfb v1.0.4 // indirect | ||||
| 	github.com/richardlehane/msoleps v1.0.3 // indirect | ||||
| @@ -68,9 +58,6 @@ require ( | ||||
| 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/subosito/gotenv v1.4.2 // indirect | ||||
| 	github.com/uptrace/bun v1.1.8 | ||||
| 	github.com/uptrace/bun/dialect/pgdialect v1.1.8 | ||||
| 	github.com/uptrace/bun/driver/pgdriver v1.1.8 | ||||
| 	github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 // indirect | ||||
| 	github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83 // indirect | ||||
| 	go.uber.org/atomic v1.11.0 // indirect | ||||
| @@ -81,6 +68,5 @@ require ( | ||||
| 	golang.org/x/sys v0.9.0 // indirect | ||||
| 	golang.org/x/text v0.10.0 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										133
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								go.sum
									
									
									
									
									
								
							| @@ -36,12 +36,10 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX | ||||
| cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | ||||
| cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= | ||||
| github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= | ||||
| github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= | ||||
| github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||||
| github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= | ||||
| github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||||
| github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= | ||||
| @@ -53,11 +51,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cockroachdb/cockroach-go/v2 v2.2.0 h1:/5znzg5n373N/3ESjHF5SMLxiW4RKB05Ql//KWfeTFs= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= | ||||
| github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= | ||||
| github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||
| github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= | ||||
| github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= | ||||
| @@ -67,14 +64,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m | ||||
| github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= | ||||
| github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= | ||||
| github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= | ||||
| github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= | ||||
| github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= | ||||
| github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= | ||||
| github.com/fufuok/utils v0.7.13 h1:FGx8Mnfg0ZB8HdVz1X60JJ2kFu1rtcsFDYUxUTzNKkU= | ||||
| github.com/fufuok/utils v0.7.13/go.mod h1:ztIaorPqZGdbvmW3YlwQp80K8rKJmEy6xa1KwpJSsmk= | ||||
| github.com/fufuok/utils v0.10.2 h1:jXgE7yBSUW9z+sJs/VQq3o4MH+jN30PzIILVXFw73lE= | ||||
| github.com/fufuok/utils v0.10.2/go.mod h1:87MJq0gAZDYBgUOpxSGoLkdv8VCuRNOL9vK02F7JC3s= | ||||
| github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU= | ||||
| @@ -83,10 +75,9 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU= | ||||
| github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= | ||||
| github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns= | ||||
| github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= | ||||
| github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= | ||||
| github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| @@ -124,7 +115,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | ||||
| github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| @@ -162,36 +153,24 @@ github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk | ||||
| github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= | ||||
| github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= | ||||
| github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= | ||||
| github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= | ||||
| github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= | ||||
| github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | ||||
| github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | ||||
| github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | ||||
| github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= | ||||
| github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | ||||
| github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= | ||||
| github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= | ||||
| github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||||
| github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0= | ||||
| github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY= | ||||
| github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||
| github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= | ||||
| github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | ||||
| github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= | ||||
| github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= | ||||
| github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= | ||||
| github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= | ||||
| github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||||
| @@ -201,7 +180,6 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP | ||||
| github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= | ||||
| github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||||
| github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | ||||
| github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | ||||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||
| @@ -214,20 +192,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= | ||||
| github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | ||||
| github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c= | ||||
| github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= | ||||
| github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ= | ||||
| github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= | ||||
| github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= | ||||
| github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= | ||||
| github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= | ||||
| github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= | ||||
| github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= | ||||
| github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= | ||||
| github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= | ||||
| github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= | ||||
| github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= | ||||
| github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= | ||||
| github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= | ||||
| @@ -243,16 +215,9 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ | ||||
| github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | ||||
| github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= | ||||
| github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= | ||||
| github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= | ||||
| github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||||
| github.com/rueian/rueidis v0.0.73 h1:+r0Z6C6HMnkquPgY3zaHVpTqmCyJL56Z36GSlyBrufk= | ||||
| github.com/rueian/rueidis v0.0.73/go.mod h1:FwnfDILF2GETrvXcYFlhIiru/7NmSIm1f+7C5kutO0I= | ||||
| github.com/rueian/rueidis v0.0.100 h1:22yp/+8YHuWc/vcrp8bkjeE7baD3vygoh2gZ2+xu1KQ= | ||||
| github.com/rueian/rueidis v0.0.100/go.mod h1:ivvsRYRtAUcf9OnheuKc5Gpa8IebrkLT1P45Lr2jlXE= | ||||
| github.com/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ= | ||||
| github.com/samber/lo v1.27.0/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= | ||||
| github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= | ||||
| github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= | ||||
| github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= | ||||
| @@ -262,75 +227,45 @@ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1Avp | ||||
| github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= | ||||
| github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= | ||||
| github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= | ||||
| github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= | ||||
| github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= | ||||
| github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= | ||||
| github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= | ||||
| github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= | ||||
| github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= | ||||
| github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= | ||||
| github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= | ||||
| github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= | ||||
| github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= | ||||
| github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= | ||||
| github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= | ||||
| github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= | ||||
| github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||||
| github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= | ||||
| github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= | ||||
| github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= | ||||
| github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= | ||||
| github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= | ||||
| github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= | ||||
| github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= | ||||
| github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= | ||||
| github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= | ||||
| github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= | ||||
| github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= | ||||
| github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA= | ||||
| github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84= | ||||
| github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8= | ||||
| github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk= | ||||
| github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o= | ||||
| github.com/uptrace/bun/driver/pgdriver v1.1.8/go.mod h1:4tHK0h7a/UoldBoe9J3GU4tEYjr3mkd62U3Kq3PVk3E= | ||||
| github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||||
| github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||||
| github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= | ||||
| github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= | ||||
| github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= | ||||
| github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= | ||||
| github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= | ||||
| github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= | ||||
| github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= | ||||
| github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= | ||||
| github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= | ||||
| github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= | ||||
| github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c= | ||||
| github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= | ||||
| github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9 h1:ge5g8vsTQclA5lXDi+PuiAFw5GMIlMHOB/5e1hsf96E= | ||||
| github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= | ||||
| github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k= | ||||
| github.com/xuri/excelize/v2 v2.6.1/go.mod h1:tL+0m6DNwSXj/sILHbQTYsLi9IF4TW59H2EF3Yrx1AU= | ||||
| github.com/xuri/excelize/v2 v2.7.1 h1:gm8q0UCAyaTt3MEF5wWMjVdmthm2EHAWesGSKS9tdVI= | ||||
| github.com/xuri/excelize/v2 v2.7.1/go.mod h1:qc0+2j4TvAUrBw36ATtcTeC1VCM0fFdAXZOmcF4nTpY= | ||||
| github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M= | ||||
| github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= | ||||
| github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83 h1:xVwnvkzzi+OiwhIkWOXvh1skFI6bagk8OvGuazM80Rw= | ||||
| github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= | ||||
| @@ -345,19 +280,11 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= | ||||
| go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||||
| go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= | ||||
| go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= | ||||
| go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= | ||||
| go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= | ||||
| go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= | ||||
| go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= | ||||
| go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= | ||||
| go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= | ||||
| go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= | ||||
| go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= | ||||
| go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= | ||||
| go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= | ||||
| go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| @@ -368,18 +295,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= | ||||
| golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= | ||||
| golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= | ||||
| golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= | ||||
| golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= | ||||
| golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= | ||||
| golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= | ||||
| golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= | ||||
| golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= | ||||
| @@ -393,14 +309,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 | ||||
| golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= | ||||
| golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= | ||||
| golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= | ||||
| golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= | ||||
| golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= | ||||
| golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= | ||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
| golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE= | ||||
| golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= | ||||
| golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= | ||||
| golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| @@ -459,15 +371,9 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v | ||||
| golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= | ||||
| golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= | ||||
| golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= | ||||
| golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= | ||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||
| golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= | ||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | ||||
| golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= | ||||
| golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= | ||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | ||||
| @@ -491,7 +397,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= | ||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= | ||||
| golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| @@ -530,23 +435,14 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w | ||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= | ||||
| golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | ||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= | ||||
| golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| @@ -561,13 +457,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= | ||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= | ||||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= | ||||
| golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||||
| @@ -720,22 +612,13 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= | ||||
| gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||
| gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| @@ -745,8 +628,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh | ||||
| honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | ||||
| honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| mellium.im/sasl v0.3.0 h1:0qoaTCTo5Py7u/g0cBIQZcMOgG/5LM71nshbXwznBh8= | ||||
| mellium.im/sasl v0.3.0/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= | ||||
| rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | ||||
| rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= | ||||
| rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= | ||||
|   | ||||
| @@ -1,163 +0,0 @@ | ||||
| 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) | ||||
| } | ||||
| @@ -1,240 +0,0 @@ | ||||
| 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) | ||||
| ); | ||||
| @@ -1,21 +0,0 @@ | ||||
| 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; | ||||
| @@ -1,31 +0,0 @@ | ||||
| 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); | ||||
| @@ -1,31 +0,0 @@ | ||||
| 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; | ||||
| @@ -1,23 +0,0 @@ | ||||
| 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); | ||||
| @@ -1,46 +0,0 @@ | ||||
| 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 | ||||
|     ); | ||||
| @@ -1,15 +0,0 @@ | ||||
| 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; | ||||
| @@ -1,37 +0,0 @@ | ||||
| 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); | ||||
| @@ -1,21 +0,0 @@ | ||||
| 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)) | ||||
| 	} | ||||
| } | ||||
| @@ -3,6 +3,7 @@ package calculate | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
| @@ -42,6 +43,11 @@ type Meter struct { | ||||
| 	Poolings                []*Pooling | ||||
| } | ||||
|  | ||||
| type PrimaryTenementStatistics struct { | ||||
| 	Tenement model.Tenement | ||||
| 	Meters   []Meter | ||||
| } | ||||
|  | ||||
| type TenementCharge struct { | ||||
| 	Tenement     string | ||||
| 	Overall      model.ConsumptionUnit | ||||
| @@ -54,6 +60,7 @@ type TenementCharge struct { | ||||
| 	LossPooled   decimal.Decimal | ||||
| 	PublicPooled decimal.Decimal | ||||
| 	FinalCharges decimal.Decimal | ||||
| 	Loss         decimal.Decimal | ||||
| 	Submeters    []*Meter | ||||
| 	Poolings     []*Meter | ||||
| } | ||||
| @@ -90,3 +97,118 @@ type PoolingSummary struct { | ||||
| 	OverallAmount     decimal.Decimal | ||||
| 	PoolingProportion decimal.Decimal | ||||
| } | ||||
|  | ||||
| func FromReportSummary(summary *model.ReportSummary, pricingMode *model.ReportIndex) Summary { | ||||
| 	var parkPrice float64 | ||||
| 	switch pricingMode.PricePolicy { | ||||
| 	case model.PRICING_POLICY_CONSUMPTION: | ||||
| 		parkPrice = summary.ConsumptionFee.Decimal.InexactFloat64() / summary.Overall.Amount.InexactFloat64() | ||||
| 	case model.PRICING_POLICY_ALL: | ||||
| 		parkPrice = summary.Overall.Fee.InexactFloat64() / summary.Overall.Amount.InexactFloat64() | ||||
| 	default: | ||||
| 		fmt.Println("无法识别类型") | ||||
| 	} | ||||
|  | ||||
| 	flatAmount := summary.Overall.Amount.InexactFloat64() - | ||||
| 		summary.Critical.Amount.InexactFloat64() - | ||||
| 		summary.Peak.Amount.InexactFloat64() - | ||||
| 		summary.Valley.Amount.InexactFloat64() | ||||
|  | ||||
| 	flatFee := summary.Overall.Amount.InexactFloat64() - | ||||
| 		summary.Critical.Fee.InexactFloat64() - | ||||
| 		summary.Peak.Fee.InexactFloat64() - | ||||
| 		summary.Valley.Fee.InexactFloat64() | ||||
|  | ||||
| 	var OverallPrice float64 | ||||
| 	if summary.Overall.Amount.GreaterThan(decimal.Zero) { | ||||
| 		OverallPrice = parkPrice | ||||
| 	} else { | ||||
| 		OverallPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var CriticalPrice float64 | ||||
| 	if summary.Critical.Amount.GreaterThan(decimal.Zero) { | ||||
| 		CriticalPrice = summary.Critical.Fee.InexactFloat64() / summary.Critical.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		CriticalPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var PeakPrice float64 | ||||
| 	if summary.Peak.Amount.GreaterThan(decimal.Zero) { | ||||
| 		PeakPrice = summary.Peak.Fee.InexactFloat64() / summary.Peak.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		PeakPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var FlatPrice float64 | ||||
| 	if decimal.NewFromFloat(flatAmount).GreaterThan(decimal.Zero) { | ||||
| 		FlatPrice = flatFee / flatAmount | ||||
| 	} else { | ||||
| 		FlatPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var ValleyPrice float64 | ||||
| 	if summary.Valley.Amount.GreaterThan(decimal.Zero) { | ||||
| 		ValleyPrice = summary.Valley.Fee.InexactFloat64() / summary.Valley.Amount.InexactFloat64() | ||||
| 	} else { | ||||
| 		ValleyPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	var LossDilutedPrice float64 | ||||
| 	if summary.Overall.Amount.GreaterThan(decimal.Zero) { | ||||
| 		LossDilutedPrice = parkPrice | ||||
| 	} else { | ||||
| 		LossDilutedPrice = decimal.Zero.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	_ = parkPrice | ||||
|  | ||||
| 	return Summary{ | ||||
| 		ReportId:    summary.ReportId, | ||||
| 		OverallArea: decimal.Zero, | ||||
| 		Overall: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Overall.Amount, | ||||
| 			Fee:        summary.Overall.Fee, | ||||
| 			Price:      decimal.NewFromFloat(OverallPrice), | ||||
| 			Proportion: decimal.NewFromFloat(1.0), | ||||
| 		}, | ||||
| 		ConsumptionFee: summary.ConsumptionFee.Decimal, | ||||
| 		Critical: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Critical.Amount, | ||||
| 			Fee:        summary.Critical.Fee, | ||||
| 			Price:      decimal.NewFromFloat(CriticalPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Critical.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Peak: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Peak.Amount, | ||||
| 			Fee:        summary.Peak.Fee, | ||||
| 			Price:      decimal.NewFromFloat(PeakPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Peak.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Flat: model.ConsumptionUnit{ | ||||
| 			Amount:     decimal.NewFromFloat(flatAmount), | ||||
| 			Fee:        decimal.NewFromFloat(flatFee), | ||||
| 			Price:      decimal.NewFromFloat(FlatPrice), | ||||
| 			Proportion: decimal.NewFromFloat(flatAmount / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Valley: model.ConsumptionUnit{ | ||||
| 			Amount:     summary.Valley.Amount, | ||||
| 			Fee:        summary.Valley.Fee, | ||||
| 			Price:      decimal.NewFromFloat(ValleyPrice), | ||||
| 			Proportion: decimal.NewFromFloat(summary.Valley.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()), | ||||
| 		}, | ||||
| 		Loss:                         decimal.Zero, | ||||
| 		LossFee:                      decimal.Zero, | ||||
| 		LossProportion:               decimal.Zero, | ||||
| 		AuthoizeLoss:                 model.ConsumptionUnit{}, | ||||
| 		BasicFee:                     summary.BasicFee, | ||||
| 		BasicPooledPriceConsumption:  decimal.Zero, | ||||
| 		BasicPooledPriceArea:         decimal.Zero, | ||||
| 		AdjustFee:                    summary.AdjustFee, | ||||
| 		AdjustPooledPriceConsumption: decimal.Zero, | ||||
| 		AdjustPooledPriceArea:        decimal.Zero, | ||||
| 		LossDilutedPrice:             decimal.NewFromFloat(LossDilutedPrice), | ||||
| 		TotalConsumption:             decimal.Zero, | ||||
| 		FinalDilutedOverall:          decimal.Zero, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,133 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type EndUserDetail struct { | ||||
| 	bun.BaseModel         `bun:"table:end_user_detail,alias:eud"` | ||||
| 	CreatedAndModified    `bun:"extend"` | ||||
| 	ReportId              string              `bun:",pk,notnull" json:"reportId"` | ||||
| 	ParkId                string              `bun:",pk,notnull" json:"parkId"` | ||||
| 	MeterId               string              `bun:"meter_04kv_id,pk,notnull" json:"meterId"` | ||||
| 	Seq                   int64               `bun:"type:bigint,notnull" json:"seq"` | ||||
| 	Ratio                 decimal.Decimal     `bun:"type:numeric,notnull" json:"ratio"` | ||||
| 	Address               *string             `json:"address"` | ||||
| 	CustomerName          *string             `json:"customerName"` | ||||
| 	ContactName           *string             `json:"contactName"` | ||||
| 	ContactPhone          *string             `json:"contactPhone"` | ||||
| 	IsPublicMeter         bool                `bun:"public_meter,notnull" json:"isPublicMeter"` | ||||
| 	LastPeriodOverall     decimal.Decimal     `bun:"type:numeric,notnull" json:"lastPeriodOverall"` | ||||
| 	LastPeriodCritical    decimal.Decimal     `bun:"type:numeric,notnull" json:"lastPeriodCritical"` | ||||
| 	LastPeriodPeak        decimal.Decimal     `bun:"type:numeric,notnull" json:"lastPeriodPeak"` | ||||
| 	LastPeriodFlat        decimal.Decimal     `bun:"type:numeric,notnull" json:"lastPeriodFlat"` | ||||
| 	LastPeriodValley      decimal.Decimal     `bun:"type:numeric,notnull" json:"lastPeriodValley"` | ||||
| 	CurrentPeriodOverall  decimal.Decimal     `bun:"type:numeric,notnull" json:"currentPeriodOverall"` | ||||
| 	CurrentPeriodCritical decimal.Decimal     `bun:"type:numeric,notnull" json:"currentPeriodCritical"` | ||||
| 	CurrentPeriodPeak     decimal.Decimal     `bun:"type:numeric,notnull" json:"currentPeriodPeak"` | ||||
| 	CurrentPeriodFlat     decimal.Decimal     `bun:"type:numeric,notnull" json:"currentPeriodFlat"` | ||||
| 	CurrentPeriodValley   decimal.Decimal     `bun:"type:numeric,notnull" json:"currentPeriodValley"` | ||||
| 	AdjustOverall         decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustOverall"` | ||||
| 	AdjustCritical        decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustCritical"` | ||||
| 	AdjustPeak            decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustPeak"` | ||||
| 	AdjustFlat            decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustFlat"` | ||||
| 	AdjustValley          decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustValley"` | ||||
| 	Overall               decimal.NullDecimal `bun:"type:numeric" json:"overall"` | ||||
| 	OverallFee            decimal.NullDecimal `bun:"type:numeric" json:"overallFee"` | ||||
| 	OverallProportion     decimal.Decimal     `bun:"type:numeric,notnull" json:"-"` | ||||
| 	Critical              decimal.NullDecimal `bun:"type:numeric" json:"critical"` | ||||
| 	CriticalFee           decimal.NullDecimal `bun:"type:numeric" json:"criticalFee"` | ||||
| 	Peak                  decimal.NullDecimal `bun:"type:numeric" json:"peak"` | ||||
| 	PeakFee               decimal.NullDecimal `bun:"type:numeric" json:"peakFee"` | ||||
| 	Flat                  decimal.NullDecimal `bun:"type:numeric" json:"flat"` | ||||
| 	FlatFee               decimal.NullDecimal `bun:"type:numeric" json:"flatFee"` | ||||
| 	Valley                decimal.NullDecimal `bun:"type:numeric" json:"valley"` | ||||
| 	ValleyFee             decimal.NullDecimal `bun:"type:numeric" json:"valleyFee"` | ||||
| 	BasicFeeDiluted       decimal.NullDecimal `bun:"type:numeric" json:"basicFeeDiluted"` | ||||
| 	AdjustFeeDiluted      decimal.NullDecimal `bun:"type:numeric" json:"adjustFeeDiluted"` | ||||
| 	LossDiluted           decimal.NullDecimal `bun:"type:numeric" json:"lossDiluted"` | ||||
| 	LossFeeDiluted        decimal.NullDecimal `bun:"type:numeric" json:"lossFeeDiluted"` | ||||
| 	FinalDiluted          decimal.NullDecimal `bun:"type:numeric" json:"finalDiluted"` | ||||
| 	FinalCharge           decimal.NullDecimal `bun:"type:numeric" json:"finalCharge"` | ||||
| 	Initialize            bool                `bun:"-" json:"-"` | ||||
| 	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:"-"` | ||||
| } | ||||
|  | ||||
| var _ bun.BeforeAppendModelHook = (*EndUserDetail)(nil) | ||||
|  | ||||
| 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) { | ||||
| 	lastPeriodSum := decimal.Sum(d.LastPeriodCritical, d.LastPeriodPeak, d.LastPeriodValley) | ||||
| 	if lastPeriodSum.GreaterThan(d.LastPeriodOverall) { | ||||
| 		return false, errors.New("上期峰谷计量总量大于上期总计电量") | ||||
| 	} | ||||
| 	currentPeriodSum := decimal.Sum(d.CurrentPeriodCritical, d.CurrentPeriodPeak, d.CurrentPeriodValley) | ||||
| 	if currentPeriodSum.GreaterThan(d.CurrentPeriodOverall) { | ||||
| 		return false, errors.New("本期峰谷计量总量大于本期总计电量") | ||||
| 	} | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| func (d *EndUserDetail) CalculatePeriod() { | ||||
| 	d.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.Overall = decimal.NewNullDecimal(d.CurrentPeriodOverall.Sub(d.LastPeriodOverall).Mul(d.Ratio).Add(d.AdjustOverall).RoundBank(2)) | ||||
| 	d.Critical = decimal.NewNullDecimal(d.CurrentPeriodCritical.Sub(d.LastPeriodCritical).Mul(d.Ratio).Add(d.AdjustCritical).RoundBank(2)) | ||||
| 	d.Peak = decimal.NewNullDecimal(d.CurrentPeriodPeak.Sub(d.LastPeriodPeak).Mul(d.Ratio).Add(d.AdjustPeak).RoundBank(2)) | ||||
| 	d.Flat = decimal.NewNullDecimal(d.CurrentPeriodFlat.Sub(d.LastPeriodFlat).Mul(d.Ratio).Add(d.AdjustFlat).RoundBank(2)) | ||||
| 	d.Valley = decimal.NewNullDecimal(d.CurrentPeriodValley.Sub(d.LastPeriodValley).Mul(d.Ratio).Add(d.AdjustValley).RoundBank(2)) | ||||
| } | ||||
|  | ||||
| type EndUserImport struct { | ||||
| 	MeterId               string              `excel:"meterId"` | ||||
| 	LastPeriodOverall     decimal.Decimal     `excel:"lastPeriodOverall"` | ||||
| 	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"` | ||||
| 	CurrentPeriodPeak     decimal.NullDecimal `excel:"currentPeriodPeak"` | ||||
| 	CurrentPeriodValley   decimal.NullDecimal `excel:"currentPeriodValley"` | ||||
| 	AdjustOverall         decimal.Decimal     `excel:"adjustOverall"` | ||||
| 	AdjustCritical        decimal.NullDecimal `excel:"adjustCritical"` | ||||
| 	AdjustPeak            decimal.NullDecimal `excel:"adjustPeak"` | ||||
| 	AdjustFlat            decimal.NullDecimal `excel:"adjustFlat"` | ||||
| 	AdjustValley          decimal.NullDecimal `excel:"adjustValley"` | ||||
| } | ||||
|  | ||||
| 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"` | ||||
| } | ||||
| @@ -1,48 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type MaintenanceFee struct { | ||||
| 	bun.BaseModel      `bun:"table:maintenance_fee,alias:m"` | ||||
| 	CreatedAndModified `bun:"extend"` | ||||
| 	Deleted            `bun:"extend"` | ||||
| 	Id                 string          `bun:",pk,notnull" json:"id"` | ||||
| 	ParkId             string          `bun:",notnull" json:"parkId"` | ||||
| 	Name               string          `bun:",notnull" json:"name"` | ||||
| 	Period             string          `bun:",notnull" json:"period"` | ||||
| 	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"` | ||||
| } | ||||
|  | ||||
| var _ bun.BeforeAppendModelHook = (*MaintenanceFee)(nil) | ||||
|  | ||||
| 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"` | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type Meter04KV struct { | ||||
| 	bun.BaseModel      `bun:"table:meter_04kv,alias:mt"` | ||||
| 	CreatedAndModified `bun:"extend"` | ||||
| 	Code               string          `bun:",pk,notnull" json:"code" excel:"code"` | ||||
| 	ParkId             string          `bun:",pk,notnull" json:"parkId"` | ||||
| 	Address            *string         `json:"address" excel:"address"` | ||||
| 	CustomerName       *string         `json:"customerName" excel:"name"` | ||||
| 	ContactName        *string         `json:"contactName" excel:"contact"` | ||||
| 	ContactPhone       *string         `json:"contactPhone" excel:"phone"` | ||||
| 	Ratio              decimal.Decimal `bun:"type:numeric,notnull" json:"ratio" excel:"ratio"` | ||||
| 	Seq                int64           `bun:"type:bigint,notnull" json:"seq" excel:"seq"` | ||||
| 	IsPublicMeter      bool            `bun:"public_meter,notnull" json:"isPublicMeter" excel:"public"` | ||||
| 	Enabled            bool            `bun:",notnull" json:"enabled"` | ||||
| 	ParkDetail         *Park           `bun:"rel:belongs-to,join:park_id=id" json:"-"` | ||||
| } | ||||
|  | ||||
| var _ bun.BeforeAppendModelHook = (*Meter04KV)(nil) | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -1,6 +1,8 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/types" | ||||
|  | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| @@ -31,3 +33,14 @@ type Park struct { | ||||
| 	LastModifiedAt   time.Time           `json:"lastModifiedAt"` | ||||
| 	DeletedAt        *time.Time          `json:"deletedAt"` | ||||
| } | ||||
|  | ||||
| type Parks struct { | ||||
| 	Park | ||||
| 	NormAuthorizedLossRate float64 `json:"norm_authorized_loss_rate"` | ||||
| } | ||||
|  | ||||
| type ParkPeriodStatistics struct { | ||||
| 	Id     string `json:"id"` | ||||
| 	Name   string `json:"name"` | ||||
| 	Period *types.DateRange | ||||
| } | ||||
|   | ||||
| @@ -1,116 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import "github.com/shopspring/decimal" | ||||
|  | ||||
| type PaidPart struct { | ||||
| 	Overall        decimal.Decimal     `json:"overall"` | ||||
| 	OverallPrice   decimal.Decimal     `json:"overallPrice"` | ||||
| 	ConsumptionFee decimal.Decimal     `json:"consumptionFee"` | ||||
| 	OverallFee     decimal.Decimal     `json:"overallFee"` | ||||
| 	Critical       decimal.NullDecimal `json:"critical"` | ||||
| 	CriticalPrice  decimal.NullDecimal `json:"criticalPrice"` | ||||
| 	CriticalFee    decimal.NullDecimal `json:"criticalFee"` | ||||
| 	Peak           decimal.NullDecimal `json:"peak"` | ||||
| 	PeakPrice      decimal.NullDecimal `json:"peakPrice"` | ||||
| 	PeakFee        decimal.NullDecimal `json:"peakFee"` | ||||
| 	Flat           decimal.NullDecimal `json:"flat"` | ||||
| 	FlatPrice      decimal.NullDecimal `json:"flatPrice"` | ||||
| 	FlatFee        decimal.NullDecimal `json:"flatFee"` | ||||
| 	Valley         decimal.NullDecimal `json:"valley"` | ||||
| 	ValleyPrice    decimal.NullDecimal `json:"valleyPrice"` | ||||
| 	ValleyFee      decimal.NullDecimal `json:"valleyFee"` | ||||
| 	BasicFee       decimal.Decimal     `json:"basicFee"` | ||||
| 	AdjustFee      decimal.Decimal     `json:"adjustFee"` | ||||
| } | ||||
|  | ||||
| type EndUserOverallPart struct { | ||||
| 	Overall       decimal.Decimal     `json:"overall"` | ||||
| 	OverallPrice  decimal.Decimal     `json:"overallPrice"` | ||||
| 	OverallFee    decimal.Decimal     `json:"consumptionFee"` | ||||
| 	Critical      decimal.NullDecimal `json:"critical"` | ||||
| 	CriticalPrice decimal.NullDecimal `json:"criticalPrice"` | ||||
| 	CriticalFee   decimal.NullDecimal `json:"criticalFee"` | ||||
| 	Peak          decimal.NullDecimal `json:"peak"` | ||||
| 	PeakPrice     decimal.NullDecimal `json:"peakPrice"` | ||||
| 	PeakFee       decimal.NullDecimal `json:"peakFee"` | ||||
| 	Flat          decimal.NullDecimal `json:"flat"` | ||||
| 	FlatPrice     decimal.NullDecimal `json:"flatPrice"` | ||||
| 	FlatFee       decimal.NullDecimal `json:"flatFee"` | ||||
| 	Valley        decimal.NullDecimal `json:"valley"` | ||||
| 	ValleyPrice   decimal.NullDecimal `json:"valleyPrice"` | ||||
| 	ValleyFee     decimal.NullDecimal `json:"valleyFee"` | ||||
| } | ||||
|  | ||||
| type ConsumptionOverallPart struct { | ||||
| 	Overall        decimal.Decimal     `json:"overall"` | ||||
| 	OverallPrice   decimal.Decimal     `json:"overallPrice"` | ||||
| 	ConsumptionFee decimal.Decimal     `json:"consumptionFee"` | ||||
| 	OverallFee     decimal.Decimal     `json:"overallFee"` | ||||
| 	Critical       decimal.NullDecimal `json:"critical"` | ||||
| 	CriticalPrice  decimal.NullDecimal `json:"criticalPrice"` | ||||
| 	CriticalFee    decimal.NullDecimal `json:"criticalFee"` | ||||
| 	Peak           decimal.NullDecimal `json:"peak"` | ||||
| 	PeakPrice      decimal.NullDecimal `json:"peakPrice"` | ||||
| 	PeakFee        decimal.NullDecimal `json:"peakFee"` | ||||
| 	Flat           decimal.NullDecimal `json:"flat"` | ||||
| 	FlatPrice      decimal.NullDecimal `json:"flatPrice"` | ||||
| 	FlatFee        decimal.NullDecimal `json:"flatFee"` | ||||
| 	Valley         decimal.NullDecimal `json:"valley"` | ||||
| 	ValleyPrice    decimal.NullDecimal `json:"valleyPrice"` | ||||
| 	ValleyFee      decimal.NullDecimal `json:"valleyFee"` | ||||
| 	Proportion     decimal.Decimal     `json:"proportion"` | ||||
| } | ||||
|  | ||||
| type LossPart struct { | ||||
| 	Quantity                decimal.Decimal `json:"quantity"` | ||||
| 	Price                   decimal.Decimal `json:"price"` | ||||
| 	ConsumptionFee          decimal.Decimal `json:"consumptionFee"` | ||||
| 	Proportion              decimal.Decimal `json:"proportion"` | ||||
| 	AuthorizeQuantity       decimal.Decimal `json:"authorizeQuantity"` | ||||
| 	AuthorizeConsumptionFee decimal.Decimal `json:"authorizeConsumptionFee"` | ||||
| } | ||||
|  | ||||
| type OtherShouldCollectionPart struct { | ||||
| 	LossFee   decimal.NullDecimal `json:"lossFee"` | ||||
| 	BasicFees decimal.Decimal     `json:"basicFees"` | ||||
| } | ||||
|  | ||||
| type MaintenancePart struct { | ||||
| 	BasicFees        decimal.Decimal `json:"basicFees"` | ||||
| 	LossFee          decimal.Decimal `json:"lossFee"` | ||||
| 	AdjustFee        decimal.Decimal `json:"adjustFee"` | ||||
| 	LossProportion   decimal.Decimal `json:"lossProportion"` | ||||
| 	AdjustProportion decimal.Decimal `json:"adjustProportion"` | ||||
| 	AdjustPrice      decimal.Decimal `json:"adjustPrice"` | ||||
| } | ||||
|  | ||||
| type EndUserSummary struct { | ||||
| 	CustomerName  *string             `json:"customerName"` | ||||
| 	Address       *string             `json:"address"` | ||||
| 	MeterId       string              `json:"meterId"` | ||||
| 	IsPublicMeter bool                `json:"isPublicMeter"` | ||||
| 	Overall       decimal.Decimal     `json:"overall"` | ||||
| 	OverallPrice  decimal.Decimal     `json:"overallPrice"` | ||||
| 	OverallFee    decimal.Decimal     `json:"overallFee"` | ||||
| 	Critical      decimal.NullDecimal `json:"critical"` | ||||
| 	CriticalFee   decimal.NullDecimal `json:"criticalFee"` | ||||
| 	Peak          decimal.NullDecimal `json:"peak"` | ||||
| 	PeakFee       decimal.NullDecimal `json:"peakFee"` | ||||
| 	Valley        decimal.NullDecimal `json:"valley"` | ||||
| 	ValleyFee     decimal.NullDecimal `json:"valleyFee"` | ||||
| 	Loss          decimal.Decimal     `json:"loss"` | ||||
| 	LossFee       decimal.Decimal     `json:"lossFee"` | ||||
| } | ||||
|  | ||||
| type Publicity struct { | ||||
| 	Report                   Report                    `json:"index"` | ||||
| 	User                     UserDetail                `json:"enterprise"` | ||||
| 	Park                     Park                      `json:"park"` | ||||
| 	Paid                     PaidPart                  `json:"paid"` | ||||
| 	EndUser                  ConsumptionOverallPart    `json:"endUserSum"` | ||||
| 	Loss                     LossPart                  `json:"loss"` | ||||
| 	PublicConsumptionOverall ConsumptionOverallPart    `json:"public"` | ||||
| 	OtherCollections         OtherShouldCollectionPart `json:"others"` | ||||
| 	Maintenance              MaintenancePart           `json:"maintenance"` | ||||
| 	EndUserDetails           []EndUserSummary          `json:"endUser"` | ||||
| } | ||||
| @@ -7,25 +7,27 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ReportIndex struct { | ||||
| 	Id                    string          `json:"id"` | ||||
| 	Park                  string          `json:"parkId" db:"park_id"` | ||||
| 	Period                types.DateRange `json:"period"` | ||||
| 	Category              int16           `json:"category"` | ||||
| 	MeterType             int16           `json:"meter04kvType" db:"meter_04kv_type"` | ||||
| 	PricePolicy           int16           `json:"pricePolicy"` | ||||
| 	BasisPooled           int16           `json:"basisPooled"` | ||||
| 	AdjustPooled          int16           `json:"adjustPooled"` | ||||
| 	LossPooled            int16           `json:"lossPooled"` | ||||
| 	PublicPooled          int16           `json:"publicPooled"` | ||||
| 	Published             bool            `json:"published"` | ||||
| 	PublishedAt           *types.DateTime `json:"publishedAt" db:"published_at"` | ||||
| 	Withdraw              int16           `json:"withdraw"` | ||||
| 	LastWithdrawAppliedAt *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"` | ||||
| 	LastWithdrawAuditAt   *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"` | ||||
| 	Status                *int16          `json:"status"` | ||||
| 	Message               *string         `json:"message"` | ||||
| 	CreatedAt             types.DateTime  `json:"createdAt" db:"created_at"` | ||||
| 	LastModifiedAt        types.DateTime  `json:"lastModifiedAt" db:"last_modified_at"` | ||||
| 	Id                          string          `json:"id"` | ||||
| 	Park                        string          `json:"parkId" db:"park_id"` | ||||
| 	Period                      types.DateRange `json:"period"` | ||||
| 	Category                    int16           `json:"category"` | ||||
| 	MeterType                   int16           `json:"meter04kvType" db:"meter_04kv_type"` | ||||
| 	PricePolicy                 int16           `json:"pricePolicy"` | ||||
| 	BasisPooled                 int16           `json:"basisPooled"` | ||||
| 	AdjustPooled                int16           `json:"adjustPooled"` | ||||
| 	LossPooled                  int16           `json:"lossPooled"` | ||||
| 	PublicPooled                int16           `json:"publicPooled"` | ||||
| 	Published                   bool            `json:"published"` | ||||
| 	PublishedAt                 *types.DateTime `json:"publishedAt" db:"published_at"` | ||||
| 	Withdraw                    int16           `json:"withdraw"` | ||||
| 	LastWithdrawAppliedAt       *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"` | ||||
| 	LastWithdrawAuditAt         *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"` | ||||
| 	Status                      *int16          `json:"status"` | ||||
| 	Message                     *string         `json:"message"` | ||||
| 	CreatedAt                   types.DateTime  `json:"createdAt" db:"created_at"` | ||||
| 	LastModifiedAt              types.DateTime  `json:"lastModifiedAt" db:"last_modified_at"` | ||||
| 	AuthorizedLossRate          float64         `json:"authorized_loss_rate" db:"authorized_loss_rate"` | ||||
| 	AuthorizedLossRateIncrement float64         `json:"authorized_loss_rate_increment" db:"authorized_loss_rate_increment"` | ||||
| } | ||||
|  | ||||
| type ReportSummary struct { | ||||
|   | ||||
| @@ -1,119 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type ReportSummary struct { | ||||
| 	bun.BaseModel                 `bun:"table:report_summary,alias:rs"` | ||||
| 	ReportId                      string              `bun:",pk,notnull" json:"-"` | ||||
| 	Overall                       decimal.Decimal     `bun:"type:numeric,notnull" json:"overall"` | ||||
| 	OverallFee                    decimal.Decimal     `bun:"type:numeric,notnull" json:"overallFee"` | ||||
| 	ConsumptionFee                decimal.NullDecimal `bun:"type:numeric" json:"consumptionFee"` | ||||
| 	OverallPrice                  decimal.NullDecimal `bun:"type:numeric" json:"overallPrice"` | ||||
| 	Critical                      decimal.Decimal     `bun:"type:numeric,notnull" json:"critical"` | ||||
| 	CriticalFee                   decimal.Decimal     `bun:"type:numeric,notnull" json:"criticalFee"` | ||||
| 	CriticalPrice                 decimal.NullDecimal `bun:"type:numeric" json:"criticalPrice"` | ||||
| 	Peak                          decimal.Decimal     `bun:"type:numeric,notnull" json:"peak"` | ||||
| 	PeakFee                       decimal.Decimal     `bun:"type:numeric,notnull" json:"peakFee"` | ||||
| 	PeakPrice                     decimal.NullDecimal `bun:"type:numeric" json:"peakPrice"` | ||||
| 	Flat                          decimal.Decimal     `bun:"type:numeric,notnull" json:"flat"` | ||||
| 	FlatFee                       decimal.Decimal     `bun:"type:numeric,notnull" json:"flatFee"` | ||||
| 	FlatPrice                     decimal.NullDecimal `bun:"type:numeric" json:"flatPrice"` | ||||
| 	Valley                        decimal.Decimal     `bun:"type:numeric,notnull" json:"valley"` | ||||
| 	ValleyFee                     decimal.Decimal     `bun:"type:numeric,notnull" json:"valleyFee"` | ||||
| 	ValleyPrice                   decimal.NullDecimal `bun:"type:numeric" json:"valleyPrice"` | ||||
| 	Loss                          decimal.NullDecimal `bun:"type:numeric" json:"loss"` | ||||
| 	LossFee                       decimal.NullDecimal `bun:"type:numeric" json:"lossFee"` | ||||
| 	LossProportion                decimal.NullDecimal `bun:"type:numeric" json:"lossProportion"` | ||||
| 	AuthorizeLoss                 decimal.NullDecimal `bun:"type:numeric" json:"authorizeLoss"` | ||||
| 	AuthorizeLossFee              decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossFee"` | ||||
| 	AuthorizeLossProportion       decimal.NullDecimal `bun:"type:numeric" json:"authorizeLossProportion"` | ||||
| 	BasicFee                      decimal.Decimal     `bun:"type:numeric,notnull" json:"basicFee"` | ||||
| 	BasicDilutedPrice             decimal.NullDecimal `bun:"type:numeric" json:"basicDilutedPrice"` | ||||
| 	AdjustFee                     decimal.Decimal     `bun:"type:numeric,notnull" json:"adjustFee"` | ||||
| 	AdjustDilutedPrice            decimal.NullDecimal `bun:"type:numeric" json:"adjustDilutedPrice"` | ||||
| 	MaintenanceDilutedPrice       decimal.NullDecimal `bun:"type:numeric" json:"maintencanceDilutedPrice"` | ||||
| 	LossDilutedPrice              decimal.NullDecimal `bun:"type:numeric" json:"lossDilutedPrice"` | ||||
| 	PublicConsumptionDilutedPrice decimal.NullDecimal `bun:"type:numeric" json:"publicConsumptionDilutedPrice"` | ||||
| 	MaintenanceOverall            decimal.NullDecimal `bun:"type:numeric" json:"maintenanceOverall"` | ||||
| 	FinalDilutedOverall           decimal.NullDecimal `bun:"type:numeric" json:"finalDilutedOverall"` | ||||
| 	Customers                     Consumptions        `bun:"type:jsonb" json:"customers"` | ||||
| 	Publics                       Consumptions        `bun:"type:jsonb" json:"publics"` | ||||
| } | ||||
|  | ||||
| type Consumptions struct { | ||||
| 	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) { | ||||
| 	amountSum := decimal.Sum(s.Critical, s.Peak, s.Valley) | ||||
| 	if amountSum.GreaterThan(s.Overall) { | ||||
| 		return false, errors.New("峰谷计量总量大于总计电量") | ||||
| 	} | ||||
| 	feeSum := decimal.Sum(s.CriticalFee, s.PeakFee, s.ValleyFee) | ||||
| 	if feeSum.GreaterThan(s.OverallFee) { | ||||
| 		return false, errors.New("峰谷计量费用大于总计费用") | ||||
| 	} | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| func (s *ReportSummary) CalculatePrices() { | ||||
| 	s.ConsumptionFee = decimal.NewNullDecimal(s.OverallFee.Sub(s.BasicFee).Sub(s.AdjustFee)) | ||||
| 	if s.Overall.GreaterThan(decimal.Zero) { | ||||
| 		s.OverallPrice = decimal.NewNullDecimal(s.ConsumptionFee.Decimal.Div(s.Overall).RoundBank(8)) | ||||
| 	} else { | ||||
| 		s.OverallPrice = decimal.NewNullDecimal(decimal.Zero) | ||||
| 	} | ||||
| 	if s.Critical.GreaterThan(decimal.Zero) { | ||||
| 		s.CriticalPrice = decimal.NewNullDecimal(s.CriticalFee.Div(s.Critical).RoundBank(8)) | ||||
| 	} else { | ||||
| 		s.CriticalPrice = decimal.NewNullDecimal(decimal.Zero) | ||||
| 	} | ||||
| 	if s.Peak.GreaterThan(decimal.Zero) { | ||||
| 		s.PeakPrice = decimal.NewNullDecimal(s.PeakFee.Div(s.Peak).RoundBank(8)) | ||||
| 	} else { | ||||
| 		s.PeakPrice = decimal.NewNullDecimal(decimal.Zero) | ||||
| 	} | ||||
| 	if s.Valley.GreaterThan(decimal.Zero) { | ||||
| 		s.ValleyPrice = decimal.NewNullDecimal(s.ValleyFee.Div(s.Valley).RoundBank(8)) | ||||
| 	} else { | ||||
| 		s.ValleyPrice = decimal.NewNullDecimal(decimal.Zero) | ||||
| 	} | ||||
| 	s.Flat = s.Overall.Sub(s.Critical).Sub(s.Peak).Sub(s.Valley) | ||||
| 	s.FlatFee = s.ConsumptionFee.Decimal.Sub(s.CriticalFee).Sub(s.PeakFee).Sub(s.ValleyFee) | ||||
| 	if s.Flat.GreaterThan(decimal.Zero) { | ||||
| 		s.FlatPrice = decimal.NewNullDecimal(s.FlatFee.Div(s.Flat).RoundBank(8)) | ||||
| 	} else { | ||||
| 		s.FlatPrice = decimal.NewNullDecimal(decimal.Zero) | ||||
| 	} | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import "time" | ||||
|  | ||||
| type Created struct { | ||||
| 	CreatedAt time.Time `bun:"type:timestamptz,notnull" json:"createdAt" time_format:"simple_datetime" time_location:"shanghai"` | ||||
| } | ||||
|  | ||||
| type CreatedWithUser struct { | ||||
| 	Created   `bun:"extend"` | ||||
| 	CreatedBy *string `json:"createdBy"` | ||||
| } | ||||
|  | ||||
| type Deleted struct { | ||||
| 	DeletedAt *time.Time `bun:"type:timestamptz,soft_delete,nullzero" json:"deletedAt" time_format:"simple_datetime" time_location:"shanghai"` | ||||
| } | ||||
|  | ||||
| type DeletedWithUser struct { | ||||
| 	Deleted   `bun:"extend"` | ||||
| 	DeletedBy *string `json:"deletedBy"` | ||||
| } | ||||
|  | ||||
| type CreatedAndModified struct { | ||||
| 	Created        `bun:"extend"` | ||||
| 	LastModifiedAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastModifiedAt" time_format:"simple_datetime" time_location:"shanghai"` | ||||
| } | ||||
|  | ||||
| type CreatedAndModifiedWithUser struct { | ||||
| 	CreatedAndModified `bun:"extend"` | ||||
| 	CreatedBy          *string `json:"createdBy"` | ||||
| 	LastModifiedBy     *string `json:"lastModifiedBy"` | ||||
| } | ||||
| @@ -21,3 +21,13 @@ type Tenement struct { | ||||
| 	LastModifiedAt types.DateTime  `json:"lastModifiedAt" db:"last_modified_at"` | ||||
| 	DeletedAt      *types.DateTime `json:"deletedAt" db:"deleted_at"` | ||||
| } | ||||
|  | ||||
| type TenementMeter struct { | ||||
| 	ParkId          string         `db:"park_id"` | ||||
| 	TenementId      string         `db:"tenement_id"` | ||||
| 	MeterId         string         `db:"meter_id"` | ||||
| 	ForeignRelation bool           `db:"foreign_relation"` | ||||
| 	AssociatedAt    types.DateTime `db:"associated_at"` | ||||
| 	DisassociatedAt types.DateTime `db:"disassociated_at"` | ||||
| 	SynchronizedAt  types.DateTime `db:"synchronized_at"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										118
									
								
								model/types.go
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								model/types.go
									
									
									
									
									
								
							| @@ -1,118 +0,0 @@ | ||||
| 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 | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type UserCharge struct { | ||||
| 	bun.BaseModel `bun:"table:user_charge,alias:c"` | ||||
| 	Created       `bun:"extend"` | ||||
| 	Seq           int64               `bun:"type:bigint,pk,notnull,autoincrement" json:"seq"` | ||||
| 	UserId        string              `bun:",notnull" json:"userId"` | ||||
| 	Fee           decimal.NullDecimal `bun:"type:numeric" json:"fee"` | ||||
| 	Discount      decimal.NullDecimal `bun:"type:numeric" json:"discount"` | ||||
| 	Amount        decimal.NullDecimal `bun:"type:numeric" json:"amount"` | ||||
| 	ChargeTo      Date                `bun:"type:date,notnull" json:"chargeTo"` | ||||
| 	Settled       bool                `bun:",notnull" json:"settled"` | ||||
| 	SettledAt     *time.Time          `bun:"type:timestamptz" json:"settledAt" time_format:"simple_datetime" time_location:"shanghai"` | ||||
| 	Cancelled     bool                `bun:",notnull" json:"cancelled"` | ||||
| 	CancelledAt   *time.Time          `bun:"type:timestamptz" json:"cancelledAt" 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:"-"` | ||||
| } | ||||
|  | ||||
| type ChargeWithName struct { | ||||
| 	UserDetail `bun:"extend"` | ||||
| 	UserCharge `bun:"extend"` | ||||
| } | ||||
|  | ||||
| var _ bun.BeforeAppendModelHook = (*UserCharge)(nil) | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -1,69 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/jinzhu/copier" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type UserDetail struct { | ||||
| 	bun.BaseModel              `bun:"table:user_detail,alias:d"` | ||||
| 	CreatedAndModifiedWithUser `bun:"extend"` | ||||
| 	DeletedWithUser            `bun:"extend"` | ||||
| 	Id                         string          `bun:",pk,notnull" json:"-"` | ||||
| 	Name                       *string         `json:"name"` | ||||
| 	Abbr                       *string         `json:"abbr"` | ||||
| 	Region                     *string         `json:"region"` | ||||
| 	Address                    *string         `json:"address"` | ||||
| 	Contact                    *string         `json:"contact"` | ||||
| 	Phone                      *string         `json:"phone"` | ||||
| 	UnitServiceFee             decimal.Decimal `bun:"type:numeric,notnull" json:"unitServiceFee"` | ||||
| 	ServiceExpiration          Date            `bun:"type:date,notnull" json:"serviceExpiration"` | ||||
| } | ||||
|  | ||||
| type JoinedUserDetail struct { | ||||
| 	UserDetail `bun:"extend"` | ||||
| 	Id         string `json:"id"` | ||||
| 	Username   string `json:"username"` | ||||
| 	Type       int8   `json:"type"` | ||||
| 	Enabled    bool   `json:"enabled"` | ||||
| } | ||||
|  | ||||
| type FullJoinedUserDetail struct { | ||||
| 	UserDetail `bun:"extend"` | ||||
| 	User       `bun:"extend"` | ||||
| } | ||||
|  | ||||
| type UserDetailSimplified struct { | ||||
| 	bun.BaseModel `bun:"table:user_detail,alias:d"` | ||||
| 	Id            string  `bun:",pk,notnull" json:"id"` | ||||
| 	Name          *string `json:"name"` | ||||
| 	Abbr          *string `json:"abbr"` | ||||
| 	Region        *string `json:"region"` | ||||
| 	Address       *string `json:"address"` | ||||
| 	Contact       *string `json:"contact"` | ||||
| 	Phone         *string `json:"phone"` | ||||
| } | ||||
|  | ||||
| func FromUserDetail(user UserDetail) UserDetailSimplified { | ||||
| 	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 | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| ) | ||||
|  | ||||
| type WillDilutedFee struct { | ||||
| 	bun.BaseModel      `bun:"table:will_diluted_fee,alias:w"` | ||||
| 	CreatedAndModified `bun:"extend"` | ||||
| 	Id                 string          `bun:",pk,notnull" json:"diluteId"` | ||||
| 	ReportId           string          `bun:",notnull" json:"reportId"` | ||||
| 	SourceId           *string         `json:"sourceId"` | ||||
| 	Name               string          `bun:",notnull" json:"name"` | ||||
| 	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"` | ||||
| } | ||||
|  | ||||
| var _ bun.BeforeAppendModelHook = (*WillDilutedFee)(nil) | ||||
|  | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										84
									
								
								model/withdraw.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								model/withdraw.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type Withdraw struct { | ||||
| 	Park   SimplifiedPark   `json:"park"` | ||||
| 	Report SimplifiedReport `json:"report"` | ||||
| 	User   UserInfos        `json:"user"` // 简易用户详细信息 | ||||
| } | ||||
|  | ||||
| // 简易园区信息 | ||||
| type SimplifiedPark struct { | ||||
| 	Address       *string `json:"address"`       // 园区地址 | ||||
| 	Area          *string `json:"area"`          // 园区面积 | ||||
| 	Capacity      *string `json:"capacity"`      // 供电容量 | ||||
| 	Category      int16   `json:"category"`      // 用电分类,0:两部制,1:单一峰谷,2:单一单一 | ||||
| 	Contact       *string `json:"contact"`       // 园区联系人 | ||||
| 	ID            string  `json:"id"`            // 园区ID | ||||
| 	Meter04KvType int16   `json:"meter04kvType"` // 户表计量类型,0:非峰谷,1:峰谷 | ||||
| 	Name          string  `json:"name"`          // 园区名称 | ||||
| 	Phone         *string `json:"phone"`         // 园区联系人电话 | ||||
| 	Region        *string `json:"region"`        // 园区所在行政区划 | ||||
| 	Tenement      *string `json:"tenement"`      // 园区住户数量 | ||||
| 	UserID        string  `json:"userId"`        // 园区所属用户ID | ||||
| } | ||||
|  | ||||
| // 简易核算报表信息 | ||||
| type SimplifiedReport struct { | ||||
| 	ID                    string  `json:"id"`                    // 报表ID | ||||
| 	LastWithdrawAppliedAt *string `json:"lastWithdrawAppliedAt"` // 最后一次申请撤回的时间,格式为 yyyy-MM-dd HH:mm:ss | ||||
| 	LastWithdrawAuditAt   *string `json:"lastWithdrawAuditAt"`   // 最后一次申请审核的时间,格式为 yyyy-MM-dd HH:mm:ss | ||||
| 	Message               *string `json:"message"`               // 当前状态的错误提示 | ||||
| 	ParkID                string  `json:"parkId"`                // 所属园区ID | ||||
| 	PeriodBegin           string  `json:"periodBegin"`           // 核算起始日期,格式为 yyyy-MM-dd | ||||
| 	PeriodEnd             string  `json:"periodEnd"`             // 核算结束日期,格式为 yyyy-MM-dd | ||||
| 	Published             bool    `json:"published"`             // 是否已发布 | ||||
| 	PublishedAt           *string `json:"publishedAt"`           // 发布时间 | ||||
| 	Status                float64 `json:"status,omitempty"`      // 当前状态,0:计算任务已队列,1:计算任务已完成,2:计算数据不足 | ||||
| 	Withdraw              int16   `json:"withdraw"`              // 报表撤回状态,0:未撤回,1:申请撤回中,2:申请拒绝,3:申请批准 | ||||
| } | ||||
|  | ||||
| // 简易用户信息 | ||||
| type UserInfos struct { | ||||
| 	Address *string `json:"address"` // 用户地址 | ||||
| 	Contact *string `json:"contact"` // 用户联系人 | ||||
| 	ID      string  `json:"id"`      // 用户ID | ||||
| 	Name    *string `json:"name"`    // 用户名称 | ||||
| 	Phone   *string `json:"phone"`   // 用户联系人电话 | ||||
| 	Region  *string `json:"region"`  // 用户所在行政区划 | ||||
| } | ||||
|  | ||||
| //用于映射数据库的报表结构体 | ||||
| type ReportRes struct { | ||||
| 	ReportId              string              `db:"report_id"` | ||||
| 	LastWithdrawAppliedAt *time.Time          `db:"last_withdraw_applied_at"` | ||||
| 	LastWithdrawAuditAt   *time.Time          `db:"last_withdraw_audit_at"` | ||||
| 	ParkID                string              `db:"report_park_id"` | ||||
| 	Period                types.DateRange     `db:"period"` | ||||
| 	Published             bool                `db:"published"` | ||||
| 	PublishedAt           *time.Time          `db: "published_at"` | ||||
| 	Withdraw              int16               `db:"withdraw"` | ||||
| 	ParkAddress           *string             `db:"park_address"` | ||||
| 	Area                  decimal.NullDecimal `db:"area"` | ||||
| 	Capacity              decimal.NullDecimal `db:"capacity"` | ||||
| 	Category              int16 | ||||
| 	ParkContact           *string             `db:"park_contact"` | ||||
| 	ParkId                string              `db:"park_id"` | ||||
| 	Meter04KVType         int16               `db:"meter_04kv_type"` | ||||
| 	ParkName              string              `db:"park_name"` | ||||
| 	ParkPhone             *string             `db:"park_phone"` | ||||
| 	ParkRegion            string              `db:"park_region"` | ||||
| 	TenementQuantity      decimal.NullDecimal `db:"tenement_quantity"` | ||||
| 	UserID                string              `db:"user_id"` | ||||
| 	Address               *string | ||||
| 	Contact               string  `db:"user_detail_contact"` | ||||
| 	ID                    string  `db:"ud_id"` | ||||
| 	Name                  *string `db:"user_detail_name"` | ||||
| 	Phone                 string  `db:"user_detail_phone"` | ||||
| 	Region                *string `db:"user_detail_region"` | ||||
| } | ||||
| @@ -4,7 +4,17 @@ import ( | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/jackc/pgx/v5" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| 	"log" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	_ "github.com/doug-martin/goqu/v9/dialect/postgres" | ||||
| @@ -68,3 +78,434 @@ func (cr _CalculateRepository) UpdateReportTaskStatus(rid string, status int16, | ||||
| 	} | ||||
| 	return res.RowsAffected() > 0, nil | ||||
| } | ||||
|  | ||||
| // 获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的 | ||||
| func (cr _CalculateRepository) GetAllPoolingMeterRelations(pid string, revokedAfter time.Time) ([]model.MeterRelation, error) { | ||||
| 	cr.log.Info("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("revokedAfter", revokedAfter)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	relationsSql, relationsArgs, _ := cr.ds. | ||||
| 		From(goqu.T("meter_relations")). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Where(goqu.Or( | ||||
| 			goqu.I("revoked_at").IsNull(), | ||||
| 			goqu.I("revoked_at").Gte(revokedAfter), | ||||
| 		)).ToSQL() | ||||
|  | ||||
| 	var meterRelation []model.MeterRelation | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, meterRelation, relationsSql, relationsArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取当前园区中所有公摊表计与商户表计之间的关联关系,包括已经解除的出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return meterRelation, nil | ||||
| } | ||||
|  | ||||
| // 获取当前园区中所有的商户与表计的关联关系,包括已经解除的 | ||||
| func (cr _CalculateRepository) GetAllTenementMeterRelations(pid string, associatedBefore time.Time, disassociatedAfter time.Time) ([]model.TenementMeter, error) { | ||||
| 	cr.log.Info("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.String("pid", pid), zap.Time("associatedBefore", associatedBefore), zap.Time("disassociatedAfter", disassociatedAfter)) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	relationsQuerySql, relationsQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("tenement_meter")). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Where(goqu.And( | ||||
| 			goqu.I("associated_at").IsNull(), | ||||
| 			goqu.I("associated_at").Lte(associatedBefore), | ||||
| 		)). | ||||
| 		Where(goqu.And( | ||||
| 			goqu.I("associated_at").IsNull(), | ||||
| 			goqu.I("associated_at").Gte(disassociatedAfter), | ||||
| 		)).ToSQL() | ||||
|  | ||||
| 	var tenementMeter []model.TenementMeter | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, tenementMeter, relationsQuerySql, relationsQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取当前园区中所有的商户与表计的关联关系,包括已经解除的", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return tenementMeter, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| // 获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据 | ||||
| func (cr _CalculateRepository) GetMeterReadings(rid string, meterType int16) ([]model.MeterReading, error) { | ||||
| 	cr.log.Info("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据", zap.String("rid", rid), zap.Int16("meterType", meterType)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	readingsQuerySql, readingsQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("meter_reading").As(goqu.I("mr"))). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("mr.meter_type").Eq(meterType), | ||||
| 			// TODO:2023.08.02 此方法出错优先查看是否这里出问题 | ||||
| 			goqu.I("mr.read_at::date <@ r.period"), | ||||
| 		). | ||||
| 		Order(goqu.I("mr.read_at").Asc()).Select(goqu.I("mr.*")).ToSQL() | ||||
|  | ||||
| 	var readings []model.MeterReading | ||||
|  | ||||
| 	err := pgxscan.Select(ctx, global.DB, readings, readingsQuerySql, readingsQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取指定报表中所有涉及到的指定类型表计在核算时间段内的所有读数数据出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return readings, nil | ||||
| } | ||||
|  | ||||
| // 获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数 | ||||
| func (cr _CalculateRepository) GetLastPeriodReadings(rid string, meterType int16) ([]model.MeterReading, error) { | ||||
| 	cr.log.Info("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数", zap.String("rid", rid), zap.Int16("meterType", meterType)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	readingsSql, readingsArgs, _ := cr.ds.From(goqu.T("meter_reading").As("mr")). | ||||
| 		Select( | ||||
| 			goqu.MAX("mr.read_at").As("read_at"), | ||||
| 			goqu.I("mr.park_id"), | ||||
| 			goqu.I("mr.meter_id"), | ||||
| 			goqu.I("mr.meter_type"), | ||||
| 			goqu.I("mr.ratio"), | ||||
| 			goqu.I("mr.overall"), | ||||
| 			goqu.I("mr.critical"), | ||||
| 			goqu.I("mr.peak"), | ||||
| 			goqu.I("mr.flat"), | ||||
| 			goqu.I("mr.valley"), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("mr.park_id"))), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("mr.meter_type").Eq(meterType), | ||||
| 			goqu.I(" mr.read_at::date <= lower(r.period)"), | ||||
| 		). | ||||
| 		GroupBy( | ||||
| 			goqu.I("mr.park_id"), | ||||
| 			goqu.I("mr.meter_id"), | ||||
| 			goqu.I("mr.meter_type"), | ||||
| 			goqu.I("mr.ratio"), | ||||
| 			goqu.I("mr.overall"), | ||||
| 			goqu.I("mr.critical"), | ||||
| 			goqu.I("mr.peak"), | ||||
| 			goqu.I("mr.flat"), | ||||
| 			goqu.I("mr.valley"), | ||||
| 			goqu.I("r.period"), | ||||
| 		).ToSQL() | ||||
|  | ||||
| 	var readings []model.MeterReading | ||||
| 	err := pgxscan.Select(ctx, global.DB, readings, readingsSql, readingsArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("获取指定报表中所有涉及到的表计在核算起始日期前的最后一次读数出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return readings, nil | ||||
| } | ||||
|  | ||||
| // 取得指定报表所涉及的所有商户信息 | ||||
| func (cr _CalculateRepository) GetAllTenements(rid string) ([]model.Tenement, error) { | ||||
| 	cr.log.Info("取得指定报表所涉及的所有商户信息", zap.String("rid", rid)) | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	tenementQuerySql, tenementQueryArgs, _ := cr.ds. | ||||
| 		From(goqu.T("tenement").As("t")). | ||||
| 		LeftJoin( | ||||
| 			goqu.T("park_building").As("b"), | ||||
| 			goqu.On(goqu.I("b.id").Eq(goqu.I("t.building"))), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("report").As("r"), | ||||
| 			goqu.On(goqu.I("r.park_id").Eq(goqu.I("t.park_id"))), | ||||
| 		). | ||||
| 		Select( | ||||
| 			goqu.I("t.*"), | ||||
| 			goqu.I("b.name").As("building_name"), | ||||
| 		). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
| 			goqu.I("t.moved_in_at <= upper(r.period)"), | ||||
| 		).ToSQL() | ||||
|  | ||||
| 	var tenements []model.Tenement | ||||
| 	err := pgxscan.Select(ctx, global.DB, tenements, tenementQuerySql, tenementQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		cr.log.Error("取得指定报表所涉及的所有商户信息出错", zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return tenements, nil | ||||
| } | ||||
| func (cr _CalculateRepository) ClearReportContent(tx pgx.Tx, rid string) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	querysql, querarg, _ := cr.ds.Delete("report_summary"). | ||||
| 		Where(goqu.C("report_id").Eq(rid)).ToSQL() | ||||
| 	_, err := tx.Exec(ctx, querysql, querarg...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	querysql, querarg, _ = cr.ds.Delete("report_public_consumption"). | ||||
| 		Where(goqu.C("report_id").Eq(rid)).ToSQL() | ||||
| 	_, err = tx.Exec(ctx, querysql, querarg...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	querysql, querarg, _ = cr.ds.Delete("report_pooled_consumption"). | ||||
| 		Where(goqu.C("report_id").Eq(rid)).ToSQL() | ||||
| 	_, err = tx.Exec(ctx, querysql, querarg...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	querysql, querarg, _ = cr.ds.Delete("report_tenement"). | ||||
| 		Where(goqu.C("report_id").Eq(rid)).ToSQL() | ||||
| 	_, err = tx.Exec(ctx, querysql, querarg...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| func (cr _CalculateRepository) SaveReportPublics(tx pgx.Tx, rid string, meters []calculate.Meter) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	if len(meters) == 0 { | ||||
| 		// 如果没有公共表计则直接返回 | ||||
| 		return nil | ||||
| 	} | ||||
| 	// 准备插入表达式 | ||||
| 	insertExpr := cr.ds.Insert("report_public_consumption"). | ||||
| 		Cols( | ||||
| 			"report_id", "park_meter_id", "overall", "critical", "peak", "flat", "valley", | ||||
| 			"loss_adjust", "consumption_total", "loss_adjust_total", "final_total", | ||||
| 		).Prepared(true) | ||||
| 	// 添加值到插入表达式中 | ||||
| 	for _, meter := range meters { | ||||
| 		insertExpr = insertExpr.Vals([]interface{}{ | ||||
| 			rid, | ||||
| 			meter.Code, | ||||
| 			meter.Overall.Fee, | ||||
| 			meter.Critical.Fee, | ||||
| 			meter.Peak.Fee, | ||||
| 			meter.Flat.Fee, | ||||
| 			meter.Valley.Fee, | ||||
| 			meter.AdjustLoss.Fee, | ||||
| 			meter.Overall.Fee, | ||||
| 			meter.AdjustLoss.Fee, | ||||
| 			meter.Overall.Fee.Add(meter.AdjustLoss.Fee), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	// 执行插入语句 | ||||
| 	inserSql, insertArgs, err := insertExpr.Prepared(true).ToSQL() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, err := tx.Exec(ctx, inserSql, insertArgs); err != nil { | ||||
| 		return fmt.Errorf("保存报表核算概要失败: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| func (cr _CalculateRepository) SaveReportSummary(tx pgx.Tx, summary calculate.Summary) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	// 构建插入表达式 | ||||
| 	insertsql, insertArgs, _ := cr.ds.Insert("report_summary"). | ||||
| 		Cols( | ||||
| 			"report_id", "overall", "critical", "peak", "flat", "valley", | ||||
| 			"loss", "loss_fee", "basic_fee", "basic_pooled_price_consumption", "basic_pooled_price_area", | ||||
| 			"adjust_fee", "adjust_pooled_price_consumption", "adjust_pooled_price_area", | ||||
| 			"loss_diluted_price", "loss_proportion", "final_diluted_overall", | ||||
| 			"consumption_fee", "authorize_loss", "overall_area", "total_consumption", | ||||
| 		). | ||||
| 		Vals(goqu.Vals{ | ||||
| 			summary.ReportId, summary.Overall, summary.Critical, summary.Peak, summary.Flat, | ||||
| 			summary.Valley, summary.Loss, summary.LossFee, summary.BasicFee, | ||||
| 			summary.BasicPooledPriceConsumption, summary.BasicPooledPriceArea, | ||||
| 			summary.AdjustFee, summary.AdjustPooledPriceConsumption, summary.AdjustPooledPriceArea, | ||||
| 			summary.LossDilutedPrice, summary.LossProportion, summary.FinalDilutedOverall, | ||||
| 			summary.ConsumptionFee, summary.AuthoizeLoss, summary.OverallArea, summary.TotalConsumption, | ||||
| 		}).Prepared(true).ToSQL() | ||||
|  | ||||
| 	// 执行插入语句 | ||||
|  | ||||
| 	if _, err := tx.Exec(ctx, insertsql, insertArgs...); err != nil { | ||||
| 		cr.log.Error("保存报表核算概要失败。") | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type NestedMeter struct { | ||||
| 	Overall     model.ConsumptionUnit | ||||
| 	Critical    model.ConsumptionUnit | ||||
| 	Peak        model.ConsumptionUnit | ||||
| 	Flat        model.ConsumptionUnit | ||||
| 	Valley      model.ConsumptionUnit | ||||
| 	CoveredArea decimal.Decimal | ||||
|  | ||||
| 	// Add other fields here as needed | ||||
| } | ||||
|  | ||||
| func (cr _CalculateRepository) SaveReportPoolings(tx pgx.Tx, | ||||
| 	rid string, | ||||
| 	meters []calculate.Meter, | ||||
| 	relations []model.MeterRelation, | ||||
| 	tenements []calculate.Meter) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	if len(meters) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	relationsSlaves := make(map[string]bool) | ||||
| 	for _, r := range relations { | ||||
| 		relationsSlaves[r.SlaveMeter] = true | ||||
| 	} | ||||
|  | ||||
| 	tenementCodes := make(map[string]bool) | ||||
| 	for _, t := range tenements { | ||||
| 		tenementCodes[t.Code] = true | ||||
| 	} | ||||
|  | ||||
| 	for _, r := range relations { | ||||
| 		if _, ok := tenementCodes[r.SlaveMeter]; !ok { | ||||
| 			return errors.New("unknown tenement meter in active meter relations") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var insertQueries []goqu.InsertDataset | ||||
| 	for _, meter := range meters { | ||||
| 		submeters := make([]NestedMeter, 0) | ||||
| 		for _, r := range relations { | ||||
| 			if r.MasterMeter == meter.Code { | ||||
| 				for _, t := range tenements { | ||||
| 					if t.Code == r.SlaveMeter { | ||||
| 						submeters = append(submeters, NestedMeter{ | ||||
| 							Overall:  t.Overall, | ||||
| 							Critical: t.Critical, | ||||
| 							Peak:     t.Peak, | ||||
| 							Flat:     t.Flat, | ||||
| 							Valley:   t.Valley, | ||||
| 						}) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		submetersJSON, err := json.Marshal(submeters) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		insertQuery := goqu.Insert("report_pooled_consumption"). | ||||
| 			Cols("report_id", "pooled_meter_id", "overall", "critical", "peak", "flat", "valley", "pooled_area", "diluted"). | ||||
| 			Vals(goqu.Vals{rid, meter.Code, meter.Overall, meter.Critical, meter.Peak, meter.Flat, meter.Valley, meter.CoveredArea, submetersJSON}) | ||||
|  | ||||
| 		insertQueries = append(insertQueries, *insertQuery) | ||||
| 	} | ||||
|  | ||||
| 	eg, _ := errgroup.WithContext(ctx) | ||||
| 	for _, insertQuery := range insertQueries { | ||||
| 		insertQuery := insertQuery // Capture loop variable | ||||
| 		eg.Go(func() error { | ||||
| 			sql, args, err := insertQuery.ToSQL() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			_, err = tx.Exec(ctx, sql, args...) | ||||
| 			return err | ||||
| 		}) | ||||
| 	} | ||||
| 	return eg.Wait() | ||||
| } | ||||
| func (cr _CalculateRepository) SaveReportTenement(tx pgx.Tx, report model.ReportIndex, tenements []model.Tenement, tenementCharges []calculate.TenementCharge) error { | ||||
| 	if len(tenements) == 0 { | ||||
| 		// 如果没有商户则直接返回 | ||||
| 		return nil | ||||
| 	} | ||||
| 	cr.log.Info("保存商户报表。") | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	insertQuery := cr.ds.Insert("report_tenement").Prepared(true) | ||||
| 	values := []goqu.Record{} | ||||
| 	for _, tenement := range tenements { | ||||
| 		charge := findTenementCharge(tenementCharges, tenement.Id) | ||||
| 		values = append(values, goqu.Record{ | ||||
| 			"report_id":         report.Id, | ||||
| 			"tenement_id":       tenement.Id, | ||||
| 			"tenement_detail":   toJSONString(tenement), | ||||
| 			"calc_period":       report.Period, | ||||
| 			"overall":           toJSONString(charge.Overall), | ||||
| 			"critical":          toJSONString(charge.Critical), | ||||
| 			"peak":              toJSONString(charge.Peak), | ||||
| 			"flat":              toJSONString(charge.Flat), | ||||
| 			"valley":            toJSONString(charge.Valley), | ||||
| 			"loss":              toJSONString(charge.Loss), | ||||
| 			"basic_fee_pooled":  charge.BasicFee, | ||||
| 			"adjust_fee_pooled": charge.AdjustFee, | ||||
| 			"loss_fee_pooled":   charge.LossPooled, | ||||
| 			"final_pooled":      charge.PublicPooled, | ||||
| 			"final_charge":      charge.FinalCharges, | ||||
| 			"meters":            toJSONString(convertToNestedMeters(charge.Submeters)), | ||||
| 			"pooled":            toJSONString(convertToNestedMeters(charge.Poolings)), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	sql, params, err := insertQuery.Rows(values).Prepared(true).ToSQL() | ||||
| 	if err != nil { | ||||
| 		log.Println("sql出现问题................................") | ||||
| 		return err | ||||
| 	} | ||||
| 	tx.Exec(ctx, sql, params...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // findTenementCharge 在 TenementCharges 切片中查找指定商户的核算内容 | ||||
| func findTenementCharge(charges []calculate.TenementCharge, tenementID string) calculate.TenementCharge { | ||||
| 	for _, charge := range charges { | ||||
| 		if charge.Tenement == tenementID { | ||||
| 			return charge | ||||
| 		} | ||||
| 	} | ||||
| 	return calculate.TenementCharge{} | ||||
| } | ||||
|  | ||||
| // convertToNestedMeters 将 Meter 切片转换为 NestedMeter 切片 | ||||
| func convertToNestedMeters(meters []*calculate.Meter) []NestedMeter { | ||||
| 	nestedMeters := []NestedMeter{} | ||||
| 	for _, meter := range meters { | ||||
| 		nestedMeters = append(nestedMeters, NestedMeter{ | ||||
| 			Overall:     meter.Overall, | ||||
| 			Critical:    meter.Critical, | ||||
| 			Peak:        meter.Peak, | ||||
| 			Flat:        meter.Flat, | ||||
| 			Valley:      meter.Valley, | ||||
| 			CoveredArea: meter.CoveredArea, | ||||
| 		}) | ||||
| 	} | ||||
| 	return nestedMeters | ||||
| } | ||||
|  | ||||
| // toJSONString 将对象转换为 JSON 字符串 | ||||
| func toJSONString(obj interface{}) string { | ||||
| 	return `"` + strings.ReplaceAll(fmt.Sprintf("%#v", obj), `"`, `\"`) + `"` | ||||
| } | ||||
|   | ||||
							
								
								
									
										449
									
								
								repository/god_mode.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										449
									
								
								repository/god_mode.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,449 @@ | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"fmt" | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	"github.com/georgysavva/scany/v2/pgxscan" | ||||
| 	"github.com/jackc/pgx/v5" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _GMRepository struct { | ||||
| 	log *zap.Logger | ||||
| 	ds  goqu.DialectWrapper | ||||
| } | ||||
|  | ||||
| var GMRepository = &_GMRepository{ | ||||
| 	log: logger.Named("Repository", "GM"), | ||||
| 	ds:  goqu.Dialect("postgres"), | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteMeterBinding(ctx context.Context, tx pgx.Tx, pid string, tenements []string, meterCodes ...[]string) error { | ||||
| 	DeleteQuery := gm.ds.From(goqu.T("tenement_meter")). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Delete() | ||||
|  | ||||
| 	if len(tenements) > 0 { | ||||
| 		DeleteQuery = DeleteQuery. | ||||
| 			Where(goqu.I("tenement_id").In(tenements)) | ||||
| 	} | ||||
|  | ||||
| 	if len(meterCodes) > 0 { | ||||
| 		DeleteQuery = DeleteQuery. | ||||
| 			Where(goqu.I("meter_id").In(meterCodes)) | ||||
| 	} | ||||
|  | ||||
| 	DeleteQuerySql, DeleteQueryArgs, _ := DeleteQuery.ToSQL() | ||||
|  | ||||
| 	_, err := tx.Exec(ctx, DeleteQuerySql, DeleteQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		gm.log.Error("数据库在删除tenement_meter表数据中出错", zap.Error(err)) | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteTenements(ctx context.Context, tx pgx.Tx, pid string, tenements ...[]string) error { | ||||
| 	DeleteTenements := gm.ds. | ||||
| 		From("tenement"). | ||||
| 		Where(goqu.I("park_id").Eq(pid)). | ||||
| 		Delete() | ||||
|  | ||||
| 	fmt.Println(len(tenements)) | ||||
| 	if len(tenements) > 0 { | ||||
| 		DeleteTenements = DeleteTenements. | ||||
| 			Where(goqu.I("id").In(tenements)) | ||||
| 	} | ||||
|  | ||||
| 	DeleteTenementsSql, DeleteTenementsArgs, _ := DeleteTenements.ToSQL() | ||||
|  | ||||
| 	_, err := tx.Exec(ctx, DeleteTenementsSql, DeleteTenementsArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		gm.log.Error("删除商户信息出错", zap.Error(err)) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteInvoices(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { | ||||
| 	if len(val) > 0 { | ||||
| 		updateQuery, updateQueryArgs, _ := gm.ds. | ||||
| 			Update(goqu.T("report_tenement")). | ||||
| 			Set(goqu.Record{"invoice": nil}). | ||||
| 			Where(goqu.I("invoice").In(val)). | ||||
| 			Where( | ||||
| 				goqu.I("report_id"). | ||||
| 					Eq( | ||||
| 						gm.ds. | ||||
| 							From(goqu.T("report")). | ||||
| 							Where(goqu.I("park_id").Eq(parks)), | ||||
| 					), | ||||
| 			).ToSQL() | ||||
| 		_, err := tx.Exec(ctx, updateQuery, updateQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			gm.log.Error("更新发票记录出错", zap.Error(err)) | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		updateQuery, updateQueryArgs, _ := gm.ds. | ||||
| 			Update(goqu.T("report_tenement")). | ||||
| 			Set(goqu.Record{"invoice": nil}). | ||||
| 			Where( | ||||
| 				goqu.I("report_id"). | ||||
| 					Eq(gm.ds. | ||||
| 						From(goqu.T("report")). | ||||
| 						Where(goqu.I("park_id").Eq(parks)), | ||||
| 					)).ToSQL() | ||||
| 		_, err := tx.Exec(ctx, updateQuery, updateQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			gm.log.Error("更新发票记录出错", zap.Error(err)) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	deleteQuery := gm.ds. | ||||
| 		From(goqu.T("invoices")). | ||||
| 		Where(goqu.I("park_id").Eq(parks)). | ||||
| 		Delete() | ||||
| 	if len(val) > 0 { | ||||
| 		deleteQuery.Where(goqu.I("invoice_code").In(val)) | ||||
| 	} | ||||
| 	deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() | ||||
|  | ||||
| 	_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		gm.log.Error("删除指定园区发票记录出错", zap.Error(err)) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
|  | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteMeterPoolings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { | ||||
| 	deleteQuery := gm.ds. | ||||
| 		Delete(goqu.T("meter_relations")). | ||||
| 		Where(goqu.I("park_id").Eq(parks)) | ||||
|  | ||||
| 	if len(val) > 0 { | ||||
| 		deleteQuery = deleteQuery. | ||||
| 			Where( | ||||
| 				goqu.I("master_meter_id").In(val), | ||||
| 				goqu.Or(goqu.I("slave_meter_id").In(val)), | ||||
| 			) | ||||
| 	} | ||||
| 	deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() | ||||
| 	_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		gm.log.Error("删除指定园区中的表计分摊关系失败", zap.Error(err)) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteMeters(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { | ||||
| 	deleteQuery := gm.ds. | ||||
| 		Delete(goqu.T("meter_04kv")). | ||||
| 		Where(goqu.I("park_id").Eq(parks)) | ||||
|  | ||||
| 	if len(val) > 0 { | ||||
| 		deleteQuery = deleteQuery.Where(goqu.I("code").In(val)) | ||||
| 	} | ||||
|  | ||||
| 	deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() | ||||
|  | ||||
| 	_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		gm.log.Error("删除指定园区的符合条件的标记出错", zap.Error(err)) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteReports(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if len(val) > 0 { | ||||
| 		deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_tenement")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_pooled_consumption")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_public_consumption")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_summary")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_task")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report")). | ||||
| 			Where(goqu.I("park_id").Eq(parks)). | ||||
| 			Where(goqu.I("id").In(val)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 	} else { | ||||
| 		deleteReportTenementQuerySql, deleteReportTenementQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_tenement")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportTenementQuerySql, deleteReportTenementQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_pooled_consumption")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportPooledConsumptionQuerySql, deleteReportPooledConsumptionQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_public_consumption")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportPublicConsumptionQuerySql, deleteReportPublicConsumptionQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_summary")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportSummaryQuerySql, deleteReportSummaryQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportTaskQuerySql, deleteReportTaskQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report_task")). | ||||
| 			Where(goqu.I("report_id").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.T("report")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)), | ||||
| 			)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportTaskQuerySql, deleteReportTaskQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		deleteReportQuerySql, deleteReportQueryArgs, _ := gm.ds. | ||||
| 			Delete(goqu.T("report")). | ||||
| 			Where(goqu.I("park_id").Eq(parks)).ToSQL() | ||||
| 		_, err = tx.Exec(ctx, deleteReportQuerySql, deleteReportQueryArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteBuildings(ctx context.Context, tx pgx.Tx, parks string, val ...[]string) error { | ||||
| 	if len(val) > 0 { | ||||
| 		updateBulidingSql, updateBlidingArgs, _ := gm.ds. | ||||
| 			Update(goqu.T("tenement")). | ||||
| 			Set(goqu.Record{"building": nil}). | ||||
| 			Where(goqu.I("park_id").Eq(parks)). | ||||
| 			Where(goqu.I("building").In( | ||||
| 				gm.ds. | ||||
| 					From(goqu.I("park_building")). | ||||
| 					Where(goqu.I("park_id").Eq(parks)). | ||||
| 					Where(goqu.I("id").In(val)). | ||||
| 					Select(goqu.I("id")), | ||||
| 			)).ToSQL() | ||||
| 		_, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		updateBulidingSql, updateBlidingArgs, _ := gm.ds. | ||||
| 			Update(goqu.T("tenement")). | ||||
| 			Set(goqu.Record{"building": nil}). | ||||
| 			Where(goqu.I("park_id").Eq(parks)).ToSQL() | ||||
| 		_, err := tx.Exec(ctx, updateBulidingSql, updateBlidingArgs...) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback(ctx) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	deleteQuery := gm.ds. | ||||
| 		Delete(goqu.I("park_building")). | ||||
| 		Where(goqu.I("park_id").Eq(parks)) | ||||
|  | ||||
| 	if len(val) > 0 { | ||||
| 		deleteQuery = deleteQuery. | ||||
| 			Where(goqu.I("id").In(val)) | ||||
| 	} | ||||
|  | ||||
| 	deleteQuerySql, deleteQueryArgs, _ := deleteQuery.ToSQL() | ||||
| 	_, err := tx.Exec(ctx, deleteQuerySql, deleteQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteParks(ctx context.Context, tx pgx.Tx, park []string) error { | ||||
| 	deleteParksSql, deleteParksArgs, _ := gm.ds. | ||||
| 		Delete(goqu.T("park")). | ||||
| 		Where(goqu.I("id").In(park)).ToSQL() | ||||
|  | ||||
| 	_, err := tx.Exec(ctx, deleteParksSql, deleteParksArgs...) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) ListAllParkIdsInUser(ctx context.Context, tx pgx.Tx, uid string) ([]string, error) { | ||||
| 	SearchParkIdsSql, SearchParkIdsArgs, _ := gm.ds. | ||||
| 		From(goqu.T("park")). | ||||
| 		Where(goqu.I("user_id").Eq(uid)). | ||||
| 		Select(goqu.I("id")).ToSQL() | ||||
| 	var pids []string | ||||
| 	err := pgxscan.Select(ctx, global.DB, &pids, SearchParkIdsSql, SearchParkIdsArgs...) | ||||
| 	if err != nil { | ||||
| 		gm.log.Error("查询["+uid+"]用户下的所有园区失败", zap.Error(err)) | ||||
| 		tx.Rollback(ctx) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return pids, nil | ||||
| } | ||||
|  | ||||
| func (gm _GMRepository) DeleteUsers(ctx context.Context, tx pgx.Tx, uid string) error { | ||||
| 	var err error | ||||
| 	//删除用户关联 | ||||
| 	DeleteUserChargeSql, DeleteUserChargeArgs, _ := gm.ds. | ||||
| 		Delete(goqu.T("user_charge")). | ||||
| 		Where(goqu.I("id").Eq(uid)).ToSQL() | ||||
|  | ||||
| 	_, err = tx.Exec(ctx,DeleteUserChargeSql,DeleteUserChargeArgs...) | ||||
| 	if err != nil { | ||||
| 		gm.log.Error("user_charge表关联出错",zap.Error(err)) | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	//删除用户详细信息 | ||||
| 	DeleteUserDetailSql, DeleteUserDetailArgs,_ := gm.ds. | ||||
| 		Delete(goqu.T("user_detail")). | ||||
| 		Where(goqu.I("id").Eq(uid)).ToSQL() | ||||
| 	_, err = tx.Exec(ctx,DeleteUserDetailSql,DeleteUserDetailArgs...) | ||||
| 	if err != nil { | ||||
| 		gm.log.Error("user_detail表详细信息出错",zap.Error(err)) | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	//删除用户基础信息 | ||||
| 	DeleteUserSql, DeleteUserArgs,_ := gm.ds. | ||||
| 		Delete(goqu.T("users")). | ||||
| 		Where(goqu.I("id").Eq(uid)).ToSQL() | ||||
| 	_, err = tx.Exec(ctx,DeleteUserSql,DeleteUserArgs...) | ||||
| 	if err != nil { | ||||
| 		gm.log.Error("user表基础信息出错",zap.Error(err)) | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -678,7 +678,7 @@ func (rr _ReportRepository) IsLastReport(rid string) (bool, error) { | ||||
| 	defer cancel() | ||||
|  | ||||
| 	checkSql, checkArgs, _ := rr.ds. | ||||
| 		From(goqu.T("report")). | ||||
| 		From(goqu.T("report").As("r")). | ||||
| 		Select(goqu.COUNT("*")). | ||||
| 		Where( | ||||
| 			goqu.I("r.id").Eq(rid), | ||||
|   | ||||
							
								
								
									
										242
									
								
								repository/withdraw.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								repository/withdraw.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/tools" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"fmt" | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	"github.com/georgysavva/scany/v2/pgxscan" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _WithdrawRepository struct { | ||||
| 	log *zap.Logger | ||||
| 	ds  goqu.DialectWrapper | ||||
| } | ||||
|  | ||||
| var WithdrawRepository = &_WithdrawRepository{ | ||||
| 	log: logger.Named("Repository", "Withdraw"), | ||||
| 	ds:  goqu.Dialect("postgres"), | ||||
| } | ||||
|  | ||||
| //该方法用于分页查询核算报表 | ||||
| func (wd _WithdrawRepository) FindWithdraw(page uint, keyword *string) ([]model.Withdraw, int64, error) { | ||||
| 	wd.log.Info("查询核算报表", zap.Stringp("keyword", keyword), zap.Int("page", int(page))) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	/** | ||||
| 	如果访问数据库次数过多出现时间过长的话可以用这个尝试优化,未测试的sql语句 | ||||
|  | ||||
| 	wd.ds.From(goqu.T("report")). | ||||
| 		Where(goqu.I("withdraw").Eq(1)). | ||||
| 		Select( | ||||
| 			goqu.I("report.*"), | ||||
| 			goqu.I("park.*"), | ||||
| 			goqu.I("user_detail.*"), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("park"), goqu.On(goqu.I("report.park_id").Eq(goqu.I("park.id"))), | ||||
| 		). | ||||
| 		Join( | ||||
| 			goqu.T("user_detail"), goqu.On(goqu.I("park.user_id").Eq(goqu.I("user_detail.id"))), | ||||
| 		).ToSQL() | ||||
|  | ||||
| 	SELECT report.*, park.*, user_detail.* | ||||
| 	FROM report as r | ||||
| 	JOIN park as p ON r.park_id = p.id | ||||
| 	JOIN user_detail as ud ON p.user_id = ud.id | ||||
| 	WHERE withdraw = 1 | ||||
| 	AND p.name Like '%keyword%' | ||||
| 	AND ud.name Like '%keyword%' | ||||
| 	*/ | ||||
| 	reportQuery := wd.ds. | ||||
| 		From(goqu.T("report").As("r")). | ||||
| 		Where(goqu.I("withdraw").Eq(1)). | ||||
| 		Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))). | ||||
| 		Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))). | ||||
| 		Where(goqu.I("p.deleted_at").IsNull()). | ||||
| 		Where(goqu.I("ud.deleted_at").IsNull()). | ||||
| 		Select( | ||||
| 			goqu.I("r.id").As("report_id"), goqu.I("r.last_withdraw_applied_at"), goqu.I("r.last_withdraw_audit_at"), | ||||
| 			goqu.I("r.park_id").As("report_park_id"), goqu.I("r.period"), goqu.I("r.published"), goqu.I("r.published_at"), goqu.I("r.withdraw"), | ||||
| 			goqu.I("p.address").As("park_address"), goqu.I("p.area"), goqu.I("p.capacity"), goqu.I("p.category"), goqu.I("p.contact").As("park_contact"), | ||||
| 			goqu.I("p.id").As("park_id"), goqu.I("p.meter_04kv_type"), goqu.I("p.name").As("park_name"), goqu.I("p.phone").As("park_phone"), goqu.I("p.region").As("park_region"), | ||||
| 			goqu.I("p.tenement_quantity"), goqu.I("p.user_id"), goqu.I("ud.address"), goqu.I("ud.contact").As("user_detail_contact"), | ||||
| 			goqu.I("ud.id").As("ud_id"), goqu.I("ud.name").As("user_detail_name"), goqu.I("ud.phone").As("user_detail_phone"), goqu.I("ud.region").As("user_detail_region"), | ||||
| 		) | ||||
|  | ||||
| 	countReportQuery := wd.ds. | ||||
| 		From(goqu.T("report").As("r")). | ||||
| 		Where(goqu.I("withdraw").Eq(1)). | ||||
| 		Join(goqu.T("park").As("p"), goqu.On(goqu.I("r.park_id").Eq(goqu.I("p.id")))). | ||||
| 		Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("p.user_id").Eq(goqu.I("ud.id")))). | ||||
| 		Select(goqu.COUNT("*")) | ||||
|  | ||||
| 	if keyword != nil && len(*keyword) > 0 { | ||||
| 		pattern := fmt.Sprintf("%%%s%%", *keyword) | ||||
| 		reportQuery = reportQuery.Where(goqu.Or( | ||||
| 			goqu.I("p.name").ILike(pattern), | ||||
| 			goqu.I("ud.name").ILike(pattern), | ||||
| 		)) | ||||
| 	} | ||||
|  | ||||
| 	reportQuery = reportQuery.Order(goqu.I("r.created_at").Desc()) | ||||
|  | ||||
| 	currentPostion := (page - 1) * config.ServiceSettings.ItemsPageSize | ||||
| 	reportQuery = reportQuery.Offset(currentPostion).Limit(config.ServiceSettings.ItemsPageSize) | ||||
|  | ||||
| 	reportSql, reportArgs, _ := reportQuery.Prepared(true).ToSQL() | ||||
|  | ||||
| 	countReportQuerySql, countReportQueryArgs, _ := countReportQuery.Prepared(true).ToSQL() | ||||
|  | ||||
| 	var ( | ||||
| 		reports []*model.ReportRes = make([]*model.ReportRes, 0) | ||||
| 		total   int64 | ||||
| 	) | ||||
| 	var err error | ||||
|  | ||||
| 	err = pgxscan.Select(ctx, global.DB, &reports, reportSql, reportArgs...) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		wd.log.Error("查询报表记录失败。", zap.Error(err)) | ||||
| 		return make([]model.Withdraw, 0), 0, err | ||||
| 	} | ||||
|  | ||||
| 	if err = pgxscan.Get(ctx, global.DB, &total, countReportQuerySql, countReportQueryArgs...); err != nil { | ||||
| 		wd.log.Error("查询报表记录总数失败。", zap.Error(err)) | ||||
| 		return make([]model.Withdraw, 0), 0, err | ||||
| 	} | ||||
|  | ||||
| 	if len(reports) <= 0 { | ||||
| 		return make([]model.Withdraw, 0), total, nil | ||||
| 	} | ||||
|  | ||||
| 	var withdrawReses []model.Withdraw | ||||
| 	//TODO: 2023.07.24对查询到的数据进行拼接(完成) | ||||
| 	for _, v := range reports { | ||||
| 		Begin := v.Period.SafeLower().Format("2006-01-02") | ||||
| 		End := v.Period.SafeUpper().Format("2006-01-02") | ||||
|  | ||||
| 		var withdrawRes model.Withdraw | ||||
| 		report := model.SimplifiedReport{ | ||||
| 			ID:                    v.ReportId, | ||||
| 			LastWithdrawAppliedAt: tools.TimeToStringPtr(v.LastWithdrawAppliedAt), | ||||
| 			LastWithdrawAuditAt:   tools.TimeToStringPtr(v.LastWithdrawAuditAt), | ||||
| 			Message:               nil, | ||||
| 			ParkID:                v.ParkID, | ||||
| 			PeriodBegin:           Begin, | ||||
| 			PeriodEnd:             End, | ||||
| 			Published:             v.Published, | ||||
| 			PublishedAt:           tools.TimeToStringPtr(v.LastWithdrawAuditAt), | ||||
| 			Status:                0., | ||||
| 			Withdraw:              v.Withdraw, | ||||
| 		} | ||||
| 		park := model.SimplifiedPark{ | ||||
| 			Address:       v.ParkAddress, | ||||
| 			Area:          tools.NullDecimalToString(v.Area), | ||||
| 			Capacity:      tools.NullDecimalToString(v.Capacity), | ||||
| 			Category:      int16(v.Category), | ||||
| 			Contact:       v.ParkContact, | ||||
| 			ID:            v.ParkId, | ||||
| 			Meter04KvType: v.Meter04KVType, | ||||
| 			Name:          v.ParkName, | ||||
| 			Phone:         v.ParkPhone, | ||||
| 			Region:        &v.ParkRegion, | ||||
| 			Tenement:      tools.NullDecimalToString(v.TenementQuantity), | ||||
| 			UserID:        v.UserID, | ||||
| 		} | ||||
| 		userInfo := model.UserInfos{ | ||||
| 			Address: v.Address, | ||||
| 			Contact: &v.Contact, | ||||
| 			ID:      v.ID, | ||||
| 			Name:    v.Name, | ||||
| 			Phone:   &v.Phone, | ||||
| 			Region:  v.Region, | ||||
| 		} | ||||
|  | ||||
| 		withdrawRes.Report = report | ||||
| 		withdrawRes.Park = park | ||||
| 		withdrawRes.User = userInfo | ||||
| 		withdrawReses = append(withdrawReses, withdrawRes) | ||||
| 	} | ||||
|  | ||||
| 	return withdrawReses, total, nil | ||||
| } | ||||
|  | ||||
| //该方法用于审核同意报表撤回 | ||||
| func (wd _WithdrawRepository) ReviewTrueReportWithdraw( rid string) (bool, error) { | ||||
| 	wd.log.Info("审核指定的报表", zap.String("rid", rid)) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	//update report set withdraw=$2, | ||||
| 	//last_withdraw_audit_at=$3, published=false, | ||||
| 	//published_at=null where id=$1 | ||||
|  | ||||
| 	tx, err := global.DB.Begin(ctx) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("开启数据库事务失败", zap.Error(err)) | ||||
| 	} | ||||
| 	updateQuerySql, updateArgs, _ := wd.ds. | ||||
| 		Update(goqu.T("report")). | ||||
| 		Set(goqu.Record{ | ||||
| 			"withdraw": model.REPORT_WITHDRAW_GRANTED, | ||||
| 			"last_withdraw_audit_at": types.Now(), | ||||
| 			"published": false, | ||||
| 			"published_at": nil, | ||||
| 		}). | ||||
| 		Where(goqu.I("id").Eq(rid)). | ||||
| 		Prepared(true).ToSQL() | ||||
|  | ||||
| 	rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("审核报表失败", zap.Error(err)) | ||||
| 		return false, err | ||||
| 	} | ||||
| 	err = tx.Commit(ctx) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("提交数据库事务失败", zap.Error(err)) | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	return rs.RowsAffected() > 0, nil | ||||
| } | ||||
|  | ||||
| //该方法用于审核拒绝报表撤回 | ||||
| func (wd _WithdrawRepository) ReviewFalseReportWithdraw( rid string) (bool, error) { | ||||
| 	wd.log.Info("审核指定的报表", zap.String("rid", rid)) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	tx, err := global.DB.Begin(ctx) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("开启数据库事务失败", zap.Error(err)) | ||||
| 	} | ||||
| 	updateQuerySql, updateArgs, _ := wd.ds. | ||||
| 		Update(goqu.T("report")). | ||||
| 		Set(goqu.Record{ | ||||
| 			"withdraw": model.REPORT_WITHDRAW_DENIED, | ||||
| 			"last_withdraw_audit_at": types.Now(), | ||||
| 			"published": false, | ||||
| 			"published_at": nil, | ||||
| 		}). | ||||
| 		Where(goqu.I("id").Eq(rid)). | ||||
| 		Prepared(true).ToSQL() | ||||
|  | ||||
| 	rs, err := tx.Exec(ctx, updateQuerySql, updateArgs...) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("审核报表失败", zap.Error(err)) | ||||
| 		return false, err | ||||
| 	} | ||||
| 	err = tx.Commit(ctx) | ||||
| 	if err != nil { | ||||
| 		wd.log.Error("提交数据库事务失败", zap.Error(err)) | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	return rs.RowsAffected() > 0, nil | ||||
| } | ||||
| @@ -16,7 +16,7 @@ type LoginResponse struct { | ||||
| func (r Result) LoginSuccess(session *model.Session) error { | ||||
| 	res := &LoginResponse{} | ||||
| 	res.Code = http.StatusOK | ||||
| 	res.Message = "用户已成功登录。" | ||||
| 	res.Message = "用户已成功登录。"+ "👋!" | ||||
| 	res.NeedReset = false | ||||
| 	res.Session = session | ||||
| 	return r.Ctx.Status(fiber.StatusOK).JSON(res) | ||||
|   | ||||
| @@ -25,24 +25,24 @@ func init() { | ||||
| } | ||||
|  | ||||
| func App() *fiber.App { | ||||
| 	app := fiber.New(fiber.Config{ | ||||
| 		BodyLimit:               30 * 1024 * 1024, | ||||
| 		EnablePrintRoutes:       true, | ||||
| 		EnableTrustedProxyCheck: false, | ||||
| 		Prefork:                 false, | ||||
| 		ErrorHandler:            errorHandler, | ||||
| 		JSONEncoder:             json.Marshal, | ||||
| 		JSONDecoder:             json.Unmarshal, | ||||
| 	app := fiber.New(fiber.Config{ //创建fiber实例的时候选择配置选项 | ||||
| 		BodyLimit:               30 * 1024 * 1024, //设置请求正文允许的最大大小。 | ||||
| 		EnablePrintRoutes:       true,             //自定义方案,用于启动消息 | ||||
| 		EnableTrustedProxyCheck: false,            //禁用受信代理 | ||||
| 		Prefork:                 false,            //禁止预处理(如果要启用预处理则需要通过shell脚本运行) | ||||
| 		ErrorHandler:            errorHandler,     //相应全局处理错误 | ||||
| 		JSONEncoder:             json.Marshal,     //json编码 | ||||
| 		JSONDecoder:             json.Unmarshal,   //json解码 | ||||
| 	}) | ||||
| 	app.Use(compress.New()) | ||||
| 	app.Use(compress.New()) //压缩中间件 | ||||
| 	app.Use(recover.New(recover.Config{ | ||||
| 		EnableStackTrace:  true, | ||||
| 		StackTraceHandler: stackTraceHandler, | ||||
| 	})) | ||||
| 	})) //恢复中间件 | ||||
| 	app.Use(logger.NewLogMiddleware(logger.LogMiddlewareConfig{ | ||||
| 		Logger: logger.Named("App"), | ||||
| 	})) | ||||
| 	app.Use(security.SessionRecovery) | ||||
| 	})) //日志中间件 | ||||
| 	app.Use(security.SessionRecovery) //会话恢复中间件 | ||||
|  | ||||
| 	controller.InitializeUserHandlers(app) | ||||
| 	controller.InitializeRegionHandlers(app) | ||||
| @@ -54,6 +54,11 @@ func App() *fiber.App { | ||||
| 	controller.InitializeTopUpHandlers(app) | ||||
| 	controller.InitializeReportHandlers(app) | ||||
|  | ||||
| 	controller.InitializeWithdrawHandlers(app)     // 公示撤回 | ||||
| 	controller.InitializeFoundationHandlers(app)   // 基础数据 | ||||
| 	controller.InitializeStatisticsController(app) // 首页信息 | ||||
| 	controller.InitializeGmController(app)         // 天神模式 | ||||
|  | ||||
| 	return app | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,262 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| type _CalculateService struct{} | ||||
|  | ||||
| var CalculateService _CalculateService | ||||
|  | ||||
| func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err error) { | ||||
| 	ctx, cancel := global.TimeoutContext(12) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	// 资料准备 | ||||
| 	var report = new(model.Report) | ||||
| 	err = global.DB.NewSelect().Model(report). | ||||
| 		Relation("Summary"). | ||||
| 		Relation("WillDilutedFees"). | ||||
| 		Relation("EndUsers"). | ||||
| 		Where("r.id = ?", reportId). | ||||
| 		Scan(ctx) | ||||
| 	if err != nil || report == nil { | ||||
| 		return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) | ||||
| 	} | ||||
|  | ||||
| 	// 综合计算 | ||||
| 	report.Summary.CalculatePrices() | ||||
|  | ||||
| 	// 计算维护费总计 | ||||
| 	maintenanceFeeTotal := decimal.NewFromInt(0) | ||||
| 	for _, m := range report.WillDilutedFees { | ||||
| 		maintenanceFeeTotal = maintenanceFeeTotal.Add(m.Fee) | ||||
| 	} | ||||
|  | ||||
| 	// 计算终端用户信息与概览中的合计 | ||||
| 	report.Summary.Customers = model.NewConsumptions() | ||||
| 	report.Summary.Publics = model.NewConsumptions() | ||||
| 	for _, eu := range report.EndUsers { | ||||
| 		eu.OverallFee = decimal.NewNullDecimal( | ||||
| 			eu.Overall.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.CriticalFee = decimal.NewNullDecimal( | ||||
| 			eu.Critical.Decimal.Mul(report.Summary.CriticalPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.PeakFee = decimal.NewNullDecimal( | ||||
| 			eu.Peak.Decimal.Mul(report.Summary.PeakPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.FlatFee = decimal.NewNullDecimal( | ||||
| 			eu.Flat.Decimal.Mul(report.Summary.FlatPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.ValleyFee = decimal.NewNullDecimal( | ||||
| 			eu.Valley.Decimal.Mul(report.Summary.ValleyPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		if eu.IsPublicMeter { | ||||
| 			report.Summary.Publics.Consumption.Decimal = report.Summary.Publics.Consumption.Decimal.Add(eu.Overall.Decimal) | ||||
| 			report.Summary.Publics.Critical.Decimal = report.Summary.Publics.Critical.Decimal.Add(eu.Critical.Decimal) | ||||
| 			report.Summary.Publics.Peak.Decimal = report.Summary.Publics.Peak.Decimal.Add(eu.Peak.Decimal) | ||||
| 			report.Summary.Publics.Flat.Decimal = report.Summary.Publics.Flat.Decimal.Add(eu.Flat.Decimal) | ||||
| 			report.Summary.Publics.Valley.Decimal = report.Summary.Publics.Valley.Decimal.Add(eu.Valley.Decimal) | ||||
| 		} else { | ||||
| 			report.Summary.Customers.Consumption.Decimal = report.Summary.Customers.Consumption.Decimal.Add(eu.Overall.Decimal) | ||||
| 			report.Summary.Customers.Critical.Decimal = report.Summary.Customers.Critical.Decimal.Add(eu.Critical.Decimal) | ||||
| 			report.Summary.Customers.Peak.Decimal = report.Summary.Customers.Peak.Decimal.Add(eu.Peak.Decimal) | ||||
| 			report.Summary.Customers.Flat.Decimal = report.Summary.Customers.Flat.Decimal.Add(eu.Flat.Decimal) | ||||
| 			report.Summary.Customers.Valley.Decimal = report.Summary.Customers.Valley.Decimal.Add(eu.Valley.Decimal) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 计算户表总电费和公共总电费以及相应的摊薄 | ||||
| 	if report.SubmeterType == model.CUSTOMER_METER_NON_PV { | ||||
| 		// 计算终端用户部分 | ||||
| 		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.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Customers.PeakFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Customers.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Customers.FlatFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Customers.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Customers.ValleyFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Customers.Valley.Decimal.Mul(report.Summary.OverallPrice.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.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Publics.PeakFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Publics.Peak.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Publics.FlatFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Publics.Flat.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		report.Summary.Publics.ValleyFee = decimal.NewNullDecimal( | ||||
| 			report.Summary.Publics.Valley.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 	} | ||||
| 	if report.SubmeterType == model.CUSTOMER_METER_PV { | ||||
| 		// 计算终端用户部分 | ||||
| 		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), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	// 计算线损 | ||||
| 	report.Summary.Loss = decimal.NewNullDecimal( | ||||
| 		report.Summary.Overall.Sub(report.Summary.Publics.Consumption.Decimal).Sub(report.Summary.Customers.Consumption.Decimal), | ||||
| 	) | ||||
| 	report.Summary.LossFee = decimal.NewNullDecimal( | ||||
| 		report.Summary.Loss.Decimal.Mul(report.Summary.OverallPrice.Decimal).RoundBank(8), | ||||
| 	) | ||||
| 	if report.Summary.Overall.Abs().GreaterThan(decimal.Zero) { | ||||
| 		report.Summary.LossProportion = decimal.NewNullDecimal( | ||||
| 			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 report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { | ||||
| 		report.Summary.LossDilutedPrice = decimal.NewNullDecimal( | ||||
| 			report.Summary.AuthorizeLossFee.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	// 计算基本电费和调整电费等的摊薄 | ||||
| 	if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { | ||||
| 		report.Summary.BasicDilutedPrice = decimal.NewNullDecimal( | ||||
| 			report.Summary.BasicFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), | ||||
| 		) | ||||
| 		report.Summary.AdjustDilutedPrice = decimal.NewNullDecimal( | ||||
| 			report.Summary.AdjustFee.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), | ||||
| 		) | ||||
| 		report.Summary.MaintenanceDilutedPrice = decimal.NewNullDecimal( | ||||
| 			maintenanceFeeTotal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(8), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	// 计算摊薄总计 | ||||
| 	report.Summary.MaintenanceOverall = decimal.NewNullDecimal(maintenanceFeeTotal) | ||||
| 	report.Summary.FinalDilutedOverall = decimal.NewNullDecimal( | ||||
| 		report.Summary.BasicFee. | ||||
| 			Add(report.Summary.AdjustFee). | ||||
| 			Add(report.Summary.AuthorizeLossFee.Decimal), | ||||
| 	) | ||||
|  | ||||
| 	// 计算终端用户的全部摊薄内容 | ||||
| 	for _, eu := range report.EndUsers { | ||||
| 		// 计算户表表计的摊薄内容 | ||||
| 		if report.Summary.Customers.Consumption.Decimal.Abs().GreaterThan(decimal.Zero) { | ||||
| 			eu.OverallProportion = eu.Overall.Decimal.Div(report.Summary.Customers.Consumption.Decimal).RoundBank(15) | ||||
| 		} else { | ||||
| 			eu.OverallProportion = decimal.Zero | ||||
| 		} | ||||
| 		eu.BasicFeeDiluted = decimal.NewNullDecimal( | ||||
| 			eu.Overall.Decimal.Mul(report.Summary.BasicDilutedPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.AdjustFeeDiluted = decimal.NewNullDecimal( | ||||
| 			eu.Overall.Decimal.Mul(report.Summary.AdjustDilutedPrice.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 		eu.LossDiluted = decimal.NewNullDecimal( | ||||
| 			report.Summary.AuthorizeLoss.Decimal.Mul(eu.OverallProportion).RoundBank(8), | ||||
| 		) | ||||
| 		eu.LossFeeDiluted = decimal.NewNullDecimal( | ||||
| 			eu.Overall.Decimal.Mul(report.Summary.LossDilutedPrice.Decimal).RoundBank(8), | ||||
| 		) | ||||
| 		eu.FinalDiluted = decimal.NewNullDecimal( | ||||
| 			eu.BasicFeeDiluted.Decimal. | ||||
| 				Add(eu.AdjustFeeDiluted.Decimal). | ||||
| 				Add(eu.LossFeeDiluted.Decimal). | ||||
| 				RoundBank(2), | ||||
| 		) | ||||
| 		eu.FinalCharge = decimal.NewNullDecimal( | ||||
| 			eu.OverallFee.Decimal.Add(eu.FinalDiluted.Decimal).RoundBank(2), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	// 向数据库保存报表概况以及终端用户摊薄结果 | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	_, err = tx.NewUpdate().Model(report.Summary).WherePK().Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return | ||||
| 	} | ||||
| 	for _, eu := range report.EndUsers { | ||||
| 		_, err = tx.NewUpdate().Model(eu).WherePK().Exec(ctx) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return | ||||
| 	} | ||||
| 	cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId)) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										33
									
								
								service/calculate/checking.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								service/calculate/checking.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
|  | ||||
| func CheckMeterArea(report *model.ReportIndex, meters []*model.MeterDetail) (bool, error) { | ||||
| 	anyAreaOptions := report.BasisPooled == model.POOLING_MODE_AREA || | ||||
| 		report.AdjustPooled == model.POOLING_MODE_AREA || | ||||
| 		report.PublicPooled == model.POOLING_MODE_AREA || | ||||
| 		report.LossPooled == model.POOLING_MODE_AREA | ||||
|  | ||||
| 	if anyAreaOptions { | ||||
| 		var meterWithoutArea int32 | ||||
|  | ||||
| 		for _, m := range meters { | ||||
| 			if (m.MeterType == model.METER_INSTALLATION_TENEMENT || m.MeterType == model.METER_INSTALLATION_POOLING) && | ||||
| 				m.Area == nil { | ||||
| 				atomic.AddInt32(&meterWithoutArea, 1) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if meterWithoutArea != 0 { | ||||
| 			return false, fmt.Errorf("园区中有 %d 个表计没有设置面积,无法进行按面积摊薄。", meterWithoutArea) | ||||
| 		} | ||||
|  | ||||
| 		return true, nil | ||||
| 	} | ||||
|  | ||||
| 	return false, nil | ||||
| } | ||||
							
								
								
									
										10
									
								
								service/calculate/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								service/calculate/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| package calculate | ||||
|  | ||||
| import "electricity_bill_calc/model/calculate" | ||||
|  | ||||
| // / 合并所有的表计 | ||||
| type Key struct { | ||||
| 	Code       string | ||||
| 	TenementID string | ||||
| } | ||||
| type MeterMap map[Key]calculate.Meter | ||||
| @@ -9,13 +9,6 @@ import ( | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| // / 合并所有的表计 | ||||
| type Key struct { | ||||
| 	Code       string | ||||
| 	TenementID string | ||||
| } | ||||
| type MeterMap map[Key]calculate.Meter | ||||
|  | ||||
| func CollectMeters(tenements []calculate.PrimaryTenementStatistics, poolings []calculate.Meter, publics []calculate.Meter) (MeterMap, error) { | ||||
| 	meters := make(MeterMap) | ||||
| 	// Collect tenement meters | ||||
|   | ||||
| @@ -1,72 +0,0 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| ) | ||||
|  | ||||
| type _ModService struct { | ||||
| 	ds goqu.DialectWrapper | ||||
| } | ||||
|  | ||||
| var ModService = _ModService{ | ||||
| 	ds: goqu.Dialect("postgres"), | ||||
| } | ||||
|  | ||||
| func mainCalculateProcess(rid string) error { | ||||
|  | ||||
| 	// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。 | ||||
| 	err := CalculateEnabledArea(tenementreports, &summary) | ||||
| 	// 计算基本电费分摊、调整电费分摊、电费摊薄单价。 | ||||
| 	err = CalculatePrices(&summary) | ||||
| 	// 收集目前所有已经处理的表计,统一对其进行摊薄计算。 | ||||
| 	collectMeters, err := CollectMeters(tenementreports, poolingmetersreports, parkmetersreports) | ||||
| 	meters, err := collectMeters, err | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// 根据核算报表中设置的摊薄内容,逐个表计进行计算 | ||||
| 	CalculateBasicPooling(report, summary, meters) | ||||
| 	CalculateAdjustPooling(report, summary, meters) | ||||
| 	CalculateLossPooling(report, summary, meters) | ||||
| 	// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。 | ||||
| 	CalculateTenementConsumptions(meters) | ||||
| 	CalculateTenementPoolings(report, summary, meters, metersrelations) | ||||
| 	// 计算商户的合计电费信息,并归总与商户相关联的表计记录 | ||||
| 	tenementCharges, err := CalculateTenementCharge(tenementReports, summary, meters, meterRelations) | ||||
| 	if err != nil { | ||||
| 		// 处理错误 | ||||
| 	} | ||||
| 	// 从此处开始向数据库保存全部计算结果。 | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	tx, _ := global.DB.Begin(ctx) | ||||
| 	err = repository.CalculateRepository.ClearReportContent(tx, report.Id) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = SaveSummary(tx, summary) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	err = SavePublics(tx, report, meters) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	err = SavePoolings(tx) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	err = SaveTenements(tx) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		return err | ||||
| 	} | ||||
| 	tx.Commit(ctx) | ||||
| } | ||||
							
								
								
									
										37
									
								
								service/calculate/park.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								service/calculate/park.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func MetersParkCalculate(report model.ReportIndex, periodStart time.Time, | ||||
| 	periodEnd time.Time, meterDetail []*model.MeterDetail, | ||||
| 	summary calculate.Summary) ([]calculate.Meter, error) { | ||||
| 	parkMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_PARK) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastTermParkMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_PARK) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	parkMeterReadings = append(parkMeterReadings, lastTermParkMeterReadings...) | ||||
|  | ||||
| 	var parkMetersReports []calculate.Meter | ||||
| 	for _, meter := range meterDetail { | ||||
| 		if meter.MeterType == model.METER_INSTALLATION_PARK { | ||||
| 			parkMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, parkMeterReadings, *meter, summary) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			parkMetersReports = append(parkMetersReports, parkMetersReport) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	return parkMetersReports, nil | ||||
| } | ||||
| @@ -60,9 +60,13 @@ func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relation | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	tx.Commit(ctx) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []*calculate.TenementCharge) error { | ||||
| func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []calculate.TenementCharge) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	var ts []model.Tenement | ||||
| 	for _, r := range tenement { | ||||
| 		ts = append(ts, r.Tenement) | ||||
| @@ -71,5 +75,7 @@ func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.Pri | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	tx.Commit(ctx) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										111
									
								
								service/calculate/pooled.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								service/calculate/pooled.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| //核算园区中的全部公摊表计的电量用量 | ||||
| func PooledMetersCalculate(report *model.ReportIndex, periodStart time.Time, | ||||
| 	periodEnd time.Time, meterDetails []*model.MeterDetail, | ||||
| 	summary calculate.Summary) ([]calculate.Meter, error) { | ||||
| 	poolingMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_POOLING) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastTermPoolingMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_POOLING) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	poolingMeterReadings = append(poolingMeterReadings, lastTermPoolingMeterReadings...) | ||||
|  | ||||
| 	var poolingMetersReports []calculate.Meter | ||||
| 	for _, meter := range meterDetails { | ||||
| 		poolingMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, poolingMeterReadings, *meter, summary) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		poolingMetersReports = append(poolingMetersReports, poolingMetersReport) | ||||
| 	} | ||||
| 	return poolingMetersReports, nil | ||||
| } | ||||
|  | ||||
| //  确定指定非商户表计在指定时间段内的全部电量 | ||||
| func determinePublicMeterConsumptions(meterId string, periodStart time.Time, | ||||
| 	periodEnd time.Time, readings []model.MeterReading, | ||||
| 	meterDetail model.MeterDetail, summary calculate.Summary) (calculate.Meter, error) { | ||||
| 	startReading, err := DeterminePublicMeterStartReading(meterId, periodStart, meterDetail.DetachedAt.Time, readings) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	endReading, err := DeterminePublicMeterEndReading(meterId, periodEnd, meterDetail.DetachedAt.Time, readings) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	overall, err := ComputeOverall(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	critical, err := ComputeCritical(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	peak, err := ComputePeak(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	flat, err := ComputeFlat(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	valley, err := ComputeValley(*startReading, *endReading, summary) | ||||
| 	if err != nil { | ||||
| 		return calculate.Meter{}, err | ||||
| 	} | ||||
|  | ||||
| 	return calculate.Meter{ | ||||
| 		Code:        meterId, | ||||
| 		Detail:      meterDetail, | ||||
| 		CoveredArea: meterDetail.Area.Decimal, | ||||
| 		LastTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{ | ||||
| 			Ratio:    startReading.Ratio, | ||||
| 			Overall:  startReading.Overall, | ||||
| 			Critical: startReading.Critical, | ||||
| 			Peak:     startReading.Peak, | ||||
| 			Flat:     startReading.Flat, | ||||
| 			Valley:   startReading.Valley, | ||||
| 		})), | ||||
| 		CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{ | ||||
| 			Ratio:    endReading.Ratio, | ||||
| 			Overall:  endReading.Overall, | ||||
| 			Critical: endReading.Critical, | ||||
| 			Peak:     endReading.Peak, | ||||
| 			Flat:     endReading.Flat, | ||||
| 			Valley:   endReading.Valley, | ||||
| 		})), | ||||
| 		Overall:                 overall, | ||||
| 		Critical:                critical, | ||||
| 		Peak:                    peak, | ||||
| 		Flat:                    flat, | ||||
| 		Valley:                  valley, | ||||
| 		AdjustLoss:              model.ConsumptionUnit{}, | ||||
| 		PooledBasic:             model.ConsumptionUnit{}, | ||||
| 		PooledAdjust:            model.ConsumptionUnit{}, | ||||
| 		PooledLoss:              model.ConsumptionUnit{}, | ||||
| 		PooledPublic:            model.ConsumptionUnit{}, | ||||
| 		SharedPoolingProportion: decimal.Decimal{}, | ||||
| 		Poolings:                nil, | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										105
									
								
								service/calculate/shared.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								service/calculate/shared.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/types" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // 确定指定非商户表计的起始读数 | ||||
| func DeterminePublicMeterStartReading(meterId string, periodStart time.Time, | ||||
| 	attachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	periodBeginning := types.Date{Time: periodStart}.ToBeginningOfDate() | ||||
|  | ||||
| 	if len(meterReadings) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var minReading types.DateTime | ||||
| 	for _, reading := range meterReadings { | ||||
| 		if reading.ReadAt.Before(minReading.Time) { | ||||
| 			minReading = reading.ReadAt | ||||
| 		} | ||||
| 	} | ||||
| 	startTimes := []time.Time{ | ||||
| 		minReading.Time, | ||||
| 		periodBeginning.Time, | ||||
| 		ShiftToAsiaShanghai(attachedAt), | ||||
| 	} | ||||
| 	if len(startTimes) < 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的起始时间", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReading []model.MeterReading | ||||
| 	for _, reading := range meterReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC()) | ||||
| 		for _, startTime := range startTimes { | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) { | ||||
| 				startReading = append(startReading, reading) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(startReading) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的起始读数", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReadings *model.MeterReading | ||||
| 	for _, readings := range startReading { | ||||
| 		if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) { | ||||
| 			startReadings = &readings | ||||
| 		} | ||||
| 	} | ||||
| 	return startReadings, nil | ||||
| } | ||||
|  | ||||
| // 确定指定非商户表计的结束读数 | ||||
| func DeterminePublicMeterEndReading(meterId string, periodEnd time.Time, | ||||
| 	detachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	periodEnding := types.Date{Time: periodEnd}.ToEndingOfDate() | ||||
|  | ||||
| 	if len(meterReadings) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var minReading types.DateTime | ||||
| 	for _, reading := range meterReadings { | ||||
| 		if reading.ReadAt.Before(minReading.Time) { | ||||
| 			minReading = reading.ReadAt | ||||
| 		} | ||||
| 	} | ||||
| 	startTimes := []time.Time{ | ||||
| 		minReading.Time, | ||||
| 		periodEnding.Time, | ||||
| 		ShiftToAsiaShanghai(detachedAt), | ||||
| 	} | ||||
| 	if len(startTimes) < 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的终止时间", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReading []model.MeterReading | ||||
| 	for _, reading := range meterReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC()) | ||||
| 		for _, startTime := range startTimes { | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) { | ||||
| 				startReading = append(startReading, reading) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(startReading) <= 0 { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的终止读数", meterId)) | ||||
| 	} | ||||
|  | ||||
| 	var startReadings *model.MeterReading | ||||
| 	for _, readings := range startReading { | ||||
| 		if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) { | ||||
| 			startReadings = &readings | ||||
| 		} | ||||
| 	} | ||||
| 	return startReadings, nil | ||||
| } | ||||
| @@ -1,18 +1,99 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"errors" | ||||
| 	"github.com/shopspring/decimal" | ||||
|  | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // / 计算已经启用的商铺面积和 | ||||
| // / | ||||
| // / - `tenements`:所有商户的电量信息 | ||||
| // / - `summary`:核算报表的摘要信息 | ||||
| func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summary *calculate.Summary) error { | ||||
| 	var areaMeters []calculate.Meter | ||||
| // 计算已经启用的商铺面积和 | ||||
| func TotalConsumptionCalculate(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary) decimal.Decimal { | ||||
| 	var areaMaters []calculate.Meter | ||||
| 	for _, t := range tenements { | ||||
| 		areaMaters = append(areaMaters, t.Meters...) | ||||
| 	} | ||||
|  | ||||
| 	areaMaters = removeDuplicates(areaMaters) | ||||
|  | ||||
| 	var areaTotal float64 | ||||
| 	for _, m := range areaMaters { | ||||
| 		areaTotal += m.Detail.Area.Decimal.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	areaTotal += summary.OverallArea.InexactFloat64() | ||||
|  | ||||
| 	return decimal.NewFromFloat(areaTotal) | ||||
|  | ||||
| } | ||||
|  | ||||
| func removeDuplicates(meters []calculate.Meter) []calculate.Meter { | ||||
| 	result := make([]calculate.Meter, 0, len(meters)) | ||||
| 	seen := make(map[string]bool) | ||||
| 	for _, meter := range meters { | ||||
| 		if !seen[meter.Code] { | ||||
| 			seen[meter.Code] = true | ||||
| 			result = append(result, meter) | ||||
| 		} | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // 计算线损以及调整线损 | ||||
| func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter, | ||||
| 	publicTotal *decimal.Decimal, summary *calculate.Summary) error { | ||||
| 	summary.Loss = summary.Overall.Amount.Sub(summary.TotalConsumption) | ||||
|  | ||||
| 	var summaryAmount decimal.Decimal | ||||
| 	if summary.Overall.Amount == decimal.Zero { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0) | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Overall.Amount | ||||
| 	} | ||||
|  | ||||
| 	summary.LossProportion = summary.Loss.Div(summaryAmount) | ||||
|  | ||||
| 	var authorizedLossRate decimal.Decimal | ||||
| 	//TODO: 2023.08.04 在此发现reportIndex结构体与数据库中的report表字段不对应缺少两个相应字段,在此添加的,如在其他地方有错误优先查找这里 | ||||
| 	if summary.LossProportion.InexactFloat64() > report.AuthorizedLossRate { | ||||
| 		authorizedLossRate = summary.LossProportion | ||||
| 	} else { | ||||
| 		return errors.New(fmt.Sprintf("经过核算园区的线损率为:{%.8f}, 核定线损率为:{%.8f}", summary.LossProportion.InexactFloat64(), authorizedLossRate.InexactFloat64())) | ||||
| 	} | ||||
|  | ||||
| 	summary.AuthoizeLoss = model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()), | ||||
| 		Fee:        decimal.NewFromFloat((summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()) * summary.Overall.Price.InexactFloat64()), | ||||
| 		Price:      summary.Overall.Price, | ||||
| 		Proportion: authorizedLossRate, | ||||
| 	} | ||||
|  | ||||
| 	differentialLoss := summary.LossDilutedPrice.Sub(summary.AuthoizeLoss.Amount) | ||||
|  | ||||
| 	if publicTotal.InexactFloat64() <= decimal.Zero.InexactFloat64() { | ||||
| 		return errors.New("园区公共表计的电量总和为非正值,或者园区未设置公共表计,无法计算核定线损") | ||||
| 	} | ||||
|  | ||||
| 	for _, meter := range *Public { | ||||
| 		amountProportion := meter.Overall.Amount.InexactFloat64() / publicTotal.InexactFloat64() | ||||
| 		adjustAmount := differentialLoss.InexactFloat64() * decimal.NewFromFloat(-1.0).InexactFloat64() | ||||
| 		meter.AdjustLoss = model.ConsumptionUnit{ | ||||
| 			Amount:     decimal.NewFromFloat(adjustAmount), | ||||
| 			Fee:        decimal.NewFromFloat(adjustAmount * summary.LossDilutedPrice.InexactFloat64()), | ||||
| 			Price:      summary.LossDilutedPrice, | ||||
| 			Proportion: decimal.NewFromFloat(amountProportion), | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 计算已经启用的商铺面积和 | ||||
| func EnabledAreaCalculate(tenements *[]calculate.PrimaryTenementStatistics, | ||||
| 	summary *calculate.Summary) (*decimal.Decimal, error) { | ||||
| 	var areaMeters []calculate.Meter | ||||
| 	for _, t := range *tenements { | ||||
| 		areaMeters = append(areaMeters, t.Meters...) | ||||
| 	} | ||||
| 	// 去重 | ||||
| @@ -32,6 +113,7 @@ func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summa | ||||
| 	return &areaTotal, nil | ||||
| } | ||||
|  | ||||
| // ================================================================================= | ||||
| // / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。 | ||||
| // / | ||||
| // / - `summary`:核算报表的摘要信息 | ||||
| @@ -52,4 +134,5 @@ func CalculatePrices(summary *calculate.Summary) error { | ||||
| 		summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea) | ||||
| 	} | ||||
| 	return nil | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,219 +3,456 @@ package calculate | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"math/big" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // / 计算各个商户的合计信息,并归总与商户关联的表计记录 | ||||
| func CalculateTenementCharge(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary, meters MeterMap, relations []model.MeterRelation) ([]calculate.TenementCharge, error) { | ||||
| 	tenementCharges := make([]calculate.TenementCharge, 0) | ||||
| // 核算园区中的全部商户表计电量用电 | ||||
| func TenementMetersCalculate(report *model.ReportIndex, | ||||
| 	PeriodStart time.Time, PeriodEnd time.Time, | ||||
| 	meterDetails []*model.MeterDetail, summary calculate.Summary) ([]calculate.PrimaryTenementStatistics, error) { | ||||
| 	tenements, err := repository.CalculateRepository.GetAllTenements(report.Id) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 0", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	tenementMeterRelations, err := repository.CalculateRepository.GetAllTenementMeterRelations(report.Park, PeriodEnd, PeriodStart) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 1", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	tenementMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_TENEMENT) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 2", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	lastPeriodReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_TENEMENT) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("tenement 3", err) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var tenementReports []calculate.PrimaryTenementStatistics | ||||
|  | ||||
| 	for _, tenement := range tenements { | ||||
| 		relatedMeters := make([]calculate.Meter, 0) | ||||
| 		for _, meter := range tenement.Meters { | ||||
| 			code := meter.Code | ||||
| 			if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT { | ||||
| 				code = meter.Detail.Code | ||||
| 		var meters []model.TenementMeter | ||||
|  | ||||
| 		for _, relation := range tenementMeterRelations { | ||||
| 			if strings.EqualFold(relation.TenementId, tenement.Id) { | ||||
| 				meters = append(meters, relation) | ||||
| 			} | ||||
| 			relatedMeter, ok := meters[Key{Code: code}] | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("related meter not found") | ||||
| 			} | ||||
| 			relatedMeters = append(relatedMeters, relatedMeter) | ||||
| 		} | ||||
|  | ||||
| 		// Calculate overall, critical, peak, flat, valley, etc. | ||||
| 		//var overall, critical, peak, flat, valley model.ConsumptionUnit | ||||
| 		basicPooled, adjustPooled, lossPooled, publicPooled := new(big.Rat), new(big.Rat), new(big.Rat), new(big.Rat) | ||||
| 		lossAmount := new(big.Rat) | ||||
| 		for _, meter := range relatedMeters { | ||||
| 			overall.Add(overall, meter.Overall) | ||||
| 			critical.Add(critical, meter.Critical) | ||||
| 			peak.Add(peak, meter.Peak) | ||||
| 			flat.Add(flat, meter.Flat) | ||||
| 			valley.Add(valley, meter.Valley) | ||||
| 			basicPooled.Add(basicPooled, meter.PooledBasic.Fee) | ||||
| 			adjustPooled.Add(adjustPooled, meter.PooledAdjust.Fee) | ||||
| 			lossAmount.Add(lossAmount, meter.AdjustLoss.Amount) | ||||
| 			lossPooled.Add(lossPooled, meter.PooledLoss.Fee) | ||||
| 			publicPooled.Add(publicPooled, meter.PooledPublic.Fee) | ||||
| 		pt, err := determineTenementConsumptions( | ||||
| 			tenement, | ||||
| 			meters, | ||||
| 			PeriodStart, | ||||
| 			PeriodEnd, | ||||
| 			tenementMeterReadings, | ||||
| 			lastPeriodReadings, | ||||
| 			meterDetails, | ||||
| 			summary, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		// Update proportions and other data for related meters | ||||
| 		for _, meter := range relatedMeters { | ||||
| 			meter.Overall.Proportion = new(big.Rat).Quo(meter.Overall.Amount, overall.Amount) | ||||
| 			meter.Critical.Proportion = new(big.Rat).Quo(meter.Critical.Amount, critical.Amount) | ||||
| 			meter.Peak.Proportion = new(big.Rat).Quo(meter.Peak.Amount, peak.Amount) | ||||
| 			meter.Flat.Proportion = new(big.Rat).Quo(meter.Flat.Amount, flat.Amount) | ||||
| 			meter.Valley.Proportion = new(big.Rat).Quo(meter.Valley.Amount, valley.Amount) | ||||
| 			meter.PooledBasic.Proportion = new(big.Rat).Quo(meter.PooledBasic.Fee, basicPooled) | ||||
| 			meter.PooledAdjust.Proportion = new(big.Rat).Quo(meter.PooledAdjust.Fee, adjustPooled) | ||||
| 			meter.PooledLoss.Proportion = new(big.Rat).Quo(meter.PooledLoss.Fee, lossPooled) | ||||
| 			meter.PooledPublic.Proportion = new(big.Rat).Quo(meter.PooledPublic.Fee, publicPooled) | ||||
| 		} | ||||
|  | ||||
| 		tenementCharges = append(tenementCharges, TenementCharges{ | ||||
| 			Tenement: tenement.Tenement.ID, | ||||
| 			Overall: ConsumptionUnit{ | ||||
| 				Amount:     overall.Amount, | ||||
| 				Fee:        new(big.Rat).Mul(overall.Amount, summary.Overall.Price), | ||||
| 				Price:      summary.Overall.Price, | ||||
| 				Proportion: new(big.Rat).Quo(overall.Amount, summary.Overall.Amount), | ||||
| 			}, | ||||
| 			Critical: ConsumptionUnit{ | ||||
| 				Amount:     critical.Amount, | ||||
| 				Fee:        new(big.Rat).Mul(critical.Amount, summary.Critical.Price), | ||||
| 				Price:      summary.Critical.Price, | ||||
| 				Proportion: new(big.Rat).Quo(critical.Amount, summary.Critical.Amount), | ||||
| 			}, | ||||
| 			Peak: ConsumptionUnit{ | ||||
| 				Amount:     peak.Amount, | ||||
| 				Fee:        new(big.Rat).Mul(peak.Amount, summary.Peak.Price), | ||||
| 				Price:      summary.Peak.Price, | ||||
| 				Proportion: new(big.Rat).Quo(peak.Amount, summary.Peak.Amount), | ||||
| 			}, | ||||
| 			Flat: ConsumptionUnit{ | ||||
| 				Amount:     flat.Amount, | ||||
| 				Fee:        new(big.Rat).Mul(flat.Amount, summary.Flat.Price), | ||||
| 				Price:      summary.Flat.Price, | ||||
| 				Proportion: new(big.Rat).Quo(flat.Amount, summary.Flat.Amount), | ||||
| 			}, | ||||
| 			Valley: ConsumptionUnit{ | ||||
| 				Amount:     valley.Amount, | ||||
| 				Fee:        new(big.Rat).Mul(valley.Amount, summary.Valley.Price), | ||||
| 				Price:      summary.Valley.Price, | ||||
| 				Proportion: new(big.Rat).Quo(valley.Amount, summary.Valley.Amount), | ||||
| 			}, | ||||
| 			Loss: ConsumptionUnit{ | ||||
| 				Amount:     lossAmount, | ||||
| 				Fee:        new(big.Rat).Mul(lossPooled, summary.AuthorizeLoss.Price), | ||||
| 				Price:      summary.AuthorizeLoss.Price, | ||||
| 				Proportion: new(big.Rat).Quo(lossAmount, summary.AuthorizeLoss.Amount), | ||||
| 			}, | ||||
| 			BasicFee:     basicPooled, | ||||
| 			AdjustFee:    adjustPooled, | ||||
| 			LossPooled:   lossPooled, | ||||
| 			PublicPooled: publicPooled, | ||||
| 			// ... 其他字段的初始化 | ||||
| 		}) | ||||
| 		tenementReports = append(tenementReports, pt) | ||||
| 	} | ||||
|  | ||||
| 	return tenementCharges, nil | ||||
| 	return tenementReports, nil | ||||
| } | ||||
| func calculateTenementCharge( | ||||
| 	tenements []*PrimaryTenementStatistics, | ||||
| 	summary *Summary, | ||||
| 	meters MeterMap, | ||||
| 	_relations []MeterRelation, | ||||
| ) ([]TenementCharges, error) { | ||||
| 	var tenementCharges []TenementCharges | ||||
|  | ||||
| // TODO: 2023.08.02 此方法未完成此方法主要用于。确定指定商户在指定时间段内的所有表计读数(完成) | ||||
| func determineTenementConsumptions(tenement model.Tenement, | ||||
| 	relatedMeters []model.TenementMeter, periodStart time.Time, | ||||
| 	periodEnd time.Time, currentTermReadings []model.MeterReading, lastPeriodReadings []model.MeterReading, | ||||
| 	meterDetails []*model.MeterDetail, summary calculate.Summary) (calculate.PrimaryTenementStatistics, error) { | ||||
| 	var meters []calculate.Meter | ||||
| 	for _, meter := range relatedMeters { | ||||
| 		startReading, err := determineTenementMeterStartReading(meter.MeterId, periodStart, ShiftToAsiaShanghai(tenement.MovedInAt.Time), meter, currentTermReadings, lastPeriodReadings) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		endReading, err := determineTenementMeterEndReading(meter.MeterId, periodEnd, ShiftToAsiaShanghai(tenement.MovedOutAt.Time), meter, currentTermReadings) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		detail, err := getMeterDetail(meterDetails, meter.MeterId) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		overall, err := ComputeOverall(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		critical, err := ComputeCritical(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		peak, err := ComputePeak(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		flat, err := ComputeFlat(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		valley, err := ComputeValley(*startReading, *endReading, summary) | ||||
| 		if err != nil { | ||||
| 			return calculate.PrimaryTenementStatistics{}, err | ||||
| 		} | ||||
|  | ||||
| 		lastTermReading := model.Reading{ | ||||
| 			Ratio:    startReading.Ratio, | ||||
| 			Overall:  startReading.Overall, | ||||
| 			Critical: startReading.Critical, | ||||
| 			Peak:     startReading.Peak, | ||||
| 			Flat:     startReading.Flat, | ||||
| 			Valley:   startReading.Valley, | ||||
| 		} | ||||
|  | ||||
| 		lastTermReadingPtr := &lastTermReading | ||||
|  | ||||
| 		currentTermReading := model.Reading{ | ||||
| 			Ratio:    endReading.Ratio, | ||||
| 			Overall:  endReading.Overall, | ||||
| 			Critical: endReading.Critical, | ||||
| 			Peak:     endReading.Peak, | ||||
| 			Flat:     endReading.Flat, | ||||
| 			Valley:   endReading.Valley, | ||||
| 		} | ||||
|  | ||||
| 		currentTermReadingPtr := ¤tTermReading | ||||
| 		meter := calculate.Meter{ | ||||
| 			Code:        meter.MeterId, | ||||
| 			Detail:      detail, | ||||
| 			CoveredArea: decimal.NewFromFloat(detail.Area.Decimal.InexactFloat64()), | ||||
|  | ||||
| 			LastTermReading:    (*calculate.Reading)(unsafe.Pointer(lastTermReadingPtr)), | ||||
| 			CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(currentTermReadingPtr)), | ||||
|  | ||||
| 			Overall:  overall, | ||||
| 			Critical: critical, | ||||
| 			Peak:     peak, | ||||
| 			Flat:     flat, | ||||
| 			Valley:   valley, | ||||
|  | ||||
| 			AdjustLoss:              model.ConsumptionUnit{}, | ||||
| 			PooledBasic:             model.ConsumptionUnit{}, | ||||
| 			PooledAdjust:            model.ConsumptionUnit{}, | ||||
| 			PooledLoss:              model.ConsumptionUnit{}, | ||||
| 			PooledPublic:            model.ConsumptionUnit{}, | ||||
| 			SharedPoolingProportion: decimal.Decimal{}, | ||||
| 			Poolings:                nil, | ||||
| 		} | ||||
|  | ||||
| 		meters = append(meters, meter) | ||||
| 	} | ||||
|  | ||||
| 	return calculate.PrimaryTenementStatistics{ | ||||
| 		Tenement: tenement, | ||||
| 		Meters:   meters, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func getMeterDetail(meterDetails []*model.MeterDetail, code string) (model.MeterDetail, error) { | ||||
| 	for _, detail := range meterDetails { | ||||
| 		if detail.Code == code { | ||||
| 			return *detail, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return model.MeterDetail{}, errors.New(fmt.Sprintf("表计 %s 的详细信息不存在", code)) | ||||
| } | ||||
|  | ||||
| // 确定指定表计的起始读数 | ||||
| func determineTenementMeterStartReading(meterId string, periodStart time.Time, tenementMovedInAt time.Time, | ||||
| 	meterRelation model.TenementMeter, currentTermReadings []model.MeterReading, | ||||
| 	lastPeriodReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	var startTime time.Time | ||||
| 	timeList := []time.Time{ | ||||
| 		periodStart, | ||||
| 		tenementMovedInAt, | ||||
| 		meterRelation.AssociatedAt.Time, | ||||
| 	} | ||||
|  | ||||
| 	for _, t := range timeList { | ||||
| 		if t.After(startTime) { | ||||
| 			startTime = t | ||||
| 		} | ||||
| 	} | ||||
| 	if startTime.IsZero() { | ||||
| 		return nil, fmt.Errorf("无法确定表计 %s 的计量的起始时间", meterId) | ||||
| 	} | ||||
|  | ||||
| 	var startReading *model.MeterReading | ||||
| 	if startTime.Equal(periodStart) { | ||||
| 		for _, reading := range lastPeriodReadings { | ||||
| 			if reading.Meter == meterId { | ||||
| 				if startReading == nil || reading.ReadAt.After(startReading.ReadAt.Time) { | ||||
| 					startReading = &reading | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, reading := range currentTermReadings { | ||||
| 			readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time) | ||||
| 			if reading.Meter == meterId && readingAt.After(startTime) { | ||||
| 				if startReading == nil || readingAt.Before(startReading.ReadAt.Time) { | ||||
| 					startReading = &reading | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if startReading == nil { | ||||
| 		return nil, errors.New("无法确定表计 " + meterId + " 的计量的起始读数") | ||||
| 	} | ||||
| 	return startReading, nil | ||||
| } | ||||
|  | ||||
| // 确定指定表计的终止读书 | ||||
| func determineTenementMeterEndReading(meterId string, periodEnd time.Time, | ||||
| 	TenementMovedOutAt time.Time, meterRelation model.TenementMeter, | ||||
| 	currentTermReadings []model.MeterReading) (*model.MeterReading, error) { | ||||
| 	var endTime time.Time | ||||
| 	timeList := []time.Time{ | ||||
| 		periodEnd, | ||||
| 		TenementMovedOutAt, | ||||
| 		ShiftToAsiaShanghai(meterRelation.DisassociatedAt.Time), | ||||
| 	} | ||||
| 	for _, t := range timeList { | ||||
| 		if t.After(endTime) { | ||||
| 			endTime = t | ||||
| 		} | ||||
| 	} | ||||
| 	if endTime.IsZero() { | ||||
| 		return nil, fmt.Errorf("无法确定表计 %s 的计量的结束时间", meterId) | ||||
| 	} | ||||
|  | ||||
| 	var endReading *model.MeterReading | ||||
|  | ||||
| 	for _, reading := range currentTermReadings { | ||||
| 		readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time) | ||||
| 		if reading.Meter == meterId && readingAt.Before(endTime) { | ||||
| 			if endReading == nil || readingAt.After(ShiftToAsiaShanghai(endReading.ReadAt.Time)) { | ||||
| 				endReading = &reading | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if endReading == nil { | ||||
| 		return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的结束读数", meterId)) | ||||
| 	} | ||||
| 	return endReading, nil | ||||
| } | ||||
|  | ||||
| func ShiftToAsiaShanghai(t time.Time) time.Time { | ||||
| 	location, _ := time.LoadLocation("Asia/Shanghai") | ||||
| 	return t.In(location) | ||||
| } | ||||
|  | ||||
| // 计算各个商户的合计信息,并归总与商户关联的表计记录 | ||||
| func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics, | ||||
| 	summary calculate.Summary, meters MeterMap) []calculate.TenementCharge { | ||||
| 	result := make(map[string][]string) | ||||
| 	for _, t := range tenements { | ||||
| 		meterCodes := make([]string, len(t.Meters)) | ||||
| 		for i, m := range t.Meters { | ||||
| 			meterCodes[i] = m.Code | ||||
| 		meterCodes := make([]string, 0) | ||||
| 		for _, m := range t.Meters { | ||||
| 			meterCodes = append(meterCodes, m.Code) | ||||
| 		} | ||||
|  | ||||
| 		relatedMeters := make([]*Meter, len(meterCodes)) | ||||
| 		for i, code := range meterCodes { | ||||
| 			relatedMeter, ok := meters[Key{Code: code, TenementID: t.Tenement.ID}] | ||||
| 			if !ok { | ||||
| 				// 处理未找到相关表计的情况 | ||||
| 				continue | ||||
| 			} | ||||
| 			relatedMeters[i] = relatedMeter | ||||
| 		} | ||||
|  | ||||
| 		var overall, critical, peak, flat, valley ConsumptionUnit | ||||
| 		var basicPooled, adjustPooled, lossAmount, lossPooled, publicPooled decimal.Decimal | ||||
|  | ||||
| 		for _, meter := range relatedMeters { | ||||
| 			overall.Amount = overall.Amount.Add(meter.Overall.Amount) | ||||
| 			overall.Fee = overall.Fee.Add(meter.Overall.Fee) | ||||
|  | ||||
| 			critical.Amount = critical.Amount.Add(meter.Critical.Amount) | ||||
| 			critical.Fee = critical.Fee.Add(meter.Critical.Fee) | ||||
|  | ||||
| 			peak.Amount = peak.Amount.Add(meter.Peak.Amount) | ||||
| 			peak.Fee = peak.Fee.Add(meter.Peak.Fee) | ||||
|  | ||||
| 			flat.Amount = flat.Amount.Add(meter.Flat.Amount) | ||||
| 			flat.Fee = flat.Fee.Add(meter.Flat.Fee) | ||||
|  | ||||
| 			valley.Amount = valley.Amount.Add(meter.Valley.Amount) | ||||
| 			valley.Fee = valley.Fee.Add(meter.Valley.Fee) | ||||
|  | ||||
| 			basicPooled = basicPooled.Add(meter.PooledBasic.Fee) | ||||
| 			adjustPooled = adjustPooled.Add(meter.PooledAdjust.Fee) | ||||
|  | ||||
| 			lossAmount = lossAmount.Add(meter.AdjustLoss.Amount) | ||||
| 			lossPooled = lossPooled.Add(meter.PooledLoss.Fee) | ||||
|  | ||||
| 			publicPooled = publicPooled.Add(meter.PooledPublic.Fee) | ||||
| 		} | ||||
|  | ||||
| 		// 反写商户表计的统计数据 | ||||
| 		for _, meter := range relatedMeters { | ||||
| 			meter.Overall.Proportion = meter.Overall.Amount.Div(overall.Amount) | ||||
| 			meter.Critical.Proportion = meter.Critical.Amount.Div(critical.Amount) | ||||
| 			meter.Peak.Proportion = meter.Peak.Amount.Div(peak.Amount) | ||||
| 			meter.Flat.Proportion = meter.Flat.Amount.Div(flat.Amount) | ||||
| 			meter.Valley.Proportion = meter.Valley.Amount.Div(valley.Amount) | ||||
| 			meter.PooledBasic.Proportion = meter.PooledBasic.Fee.Div(basicPooled) | ||||
| 			meter.PooledAdjust.Proportion = meter.PooledAdjust.Fee.Div(adjustPooled) | ||||
| 			meter.PooledLoss.Proportion = meter.PooledLoss.Fee.Div(lossPooled) | ||||
| 			meter.PooledPublic.Proportion = meter.PooledPublic.Fee.Div(publicPooled) | ||||
| 		} | ||||
|  | ||||
| 		// 构造并添加商户的合计信息 | ||||
| 		tenementCharges = append(tenementCharges, TenementCharges{ | ||||
| 			Tenement: t.Tenement.ID, | ||||
| 			Overall: ConsumptionUnit{ | ||||
| 				Price:      summary.Overall.Price, | ||||
| 				Proportion: overall.Amount.Div(summary.Overall.Amount), | ||||
| 				Amount:     overall.Amount, | ||||
| 				Fee:        overall.Fee, | ||||
| 			}, | ||||
| 			Critical: ConsumptionUnit{ | ||||
| 				Price:      summary.Critical.Price, | ||||
| 				Proportion: critical.Amount.Div(summary.Critical.Amount), | ||||
| 				Amount:     critical.Amount, | ||||
| 				Fee:        critical.Fee, | ||||
| 			}, | ||||
| 			Peak: ConsumptionUnit{ | ||||
| 				Price:      summary.Peak.Price, | ||||
| 				Proportion: peak.Amount.Div(summary.Peak.Amount), | ||||
| 				Amount:     peak.Amount, | ||||
| 				Fee:        peak.Fee, | ||||
| 			}, | ||||
| 			Flat: ConsumptionUnit{ | ||||
| 				Price:      summary.Flat.Price, | ||||
| 				Proportion: flat.Amount.Div(summary.Flat.Amount), | ||||
| 				Amount:     flat.Amount, | ||||
| 				Fee:        flat.Fee, | ||||
| 			}, | ||||
| 			Valley: ConsumptionUnit{ | ||||
| 				Price:      summary.Valley.Price, | ||||
| 				Proportion: valley.Amount.Div(summary.Valley.Amount), | ||||
| 				Amount:     valley.Amount, | ||||
| 				Fee:        valley.Fee, | ||||
| 			}, | ||||
| 			Loss: ConsumptionUnit{ | ||||
| 				Price:      summary.AuthorizeLoss.Price, | ||||
| 				Proportion: lossAmount.Div(summary.AuthorizeLoss.Amount), | ||||
| 				Amount:     lossAmount, | ||||
| 				Fee:        lossPooled, | ||||
| 			}, | ||||
| 			BasicFee:     basicPooled, | ||||
| 			AdjustFee:    adjustPooled, | ||||
| 			LossPooled:   lossPooled, | ||||
| 			PublicPooled: publicPooled, | ||||
| 			FinalCharges: overall.Fee.Add(basicPooled).Add(adjustPooled).Add(lossPooled).Add(publicPooled), | ||||
| 			Submeters:    relatedMeters, | ||||
| 			Poolings:     make([]Meter, 0), // TODO: Add pooling logic here | ||||
| 		}) | ||||
| 		sort.Strings(meterCodes) | ||||
| 		result[t.Tenement.Id] = meterCodes | ||||
| 	} | ||||
| 	var Key Key | ||||
| 	var tc []calculate.TenementCharge | ||||
| 	for tCode, meterCodes := range result { | ||||
| 		relatedMeters := make([]calculate.Meter, 0) | ||||
| 		for _, code := range meterCodes { | ||||
| 			Key.Code = code + "_" + tCode | ||||
| 			meter, ok := meters[Key] | ||||
| 			if ok { | ||||
| 				relatedMeters = append(relatedMeters, meter) | ||||
| 			} | ||||
| 		} | ||||
| 		// 计算商户的合计电费信息 | ||||
| 		var overall model.ConsumptionUnit | ||||
| 		var critical model.ConsumptionUnit | ||||
| 		var peak model.ConsumptionUnit | ||||
| 		var flat model.ConsumptionUnit | ||||
| 		var valley model.ConsumptionUnit | ||||
|  | ||||
| 	return tenementCharges, nil | ||||
| 		var basicPooled decimal.Decimal | ||||
| 		var adjustPooled decimal.Decimal | ||||
| 		var lossAmount decimal.Decimal | ||||
| 		var lossPooled decimal.Decimal | ||||
| 		var publicPooled decimal.Decimal | ||||
|  | ||||
| 		for _, meter := range relatedMeters { | ||||
| 			overall.Amount.Add(meter.Overall.Amount) | ||||
| 			overall.Fee.Add(meter.Overall.Fee) | ||||
|  | ||||
| 			critical.Amount.Add(meter.Critical.Amount) | ||||
| 			critical.Fee.Add(meter.Critical.Fee) | ||||
|  | ||||
| 			peak.Amount.Add(meter.Peak.Amount) | ||||
| 			peak.Fee.Add(meter.Peak.Fee) | ||||
|  | ||||
| 			flat.Amount.Add(meter.Flat.Amount) | ||||
| 			flat.Fee.Add(meter.Flat.Fee) | ||||
|  | ||||
| 			valley.Amount.Add(meter.Valley.Amount) | ||||
| 			valley.Fee.Add(meter.Valley.Fee) | ||||
|  | ||||
| 			basicPooled.Add(meter.PooledBasic.Fee) | ||||
| 			adjustPooled.Add(meter.PooledAdjust.Fee) | ||||
| 			lossAmount.Add(meter.PooledLoss.Amount) | ||||
| 			lossPooled.Add(meter.PooledLoss.Fee) | ||||
| 			publicPooled.Add(meter.PooledPublic.Fee) | ||||
|  | ||||
| 			// 反写商户表计的统计数据 | ||||
| 			meter.Overall.Proportion = func() decimal.Decimal { | ||||
| 				if overall.Amount.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.Overall.Amount.Div(overall.Amount) | ||||
| 			}() | ||||
| 			meter.Critical.Proportion = func() decimal.Decimal { | ||||
| 				if critical.Amount.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.Critical.Amount.Div(critical.Amount) | ||||
| 			}() | ||||
| 			meter.Peak.Proportion = func() decimal.Decimal { | ||||
| 				if peak.Amount.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.Peak.Amount.Div(peak.Amount) | ||||
| 			}() | ||||
| 			meter.Flat.Proportion = func() decimal.Decimal { | ||||
| 				if flat.Amount.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.Flat.Amount.Div(flat.Amount) | ||||
| 			}() | ||||
| 			meter.Valley.Proportion = func() decimal.Decimal { | ||||
| 				if valley.Amount.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.Valley.Amount.Div(valley.Amount) | ||||
| 			}() | ||||
| 			meter.PooledBasic.Proportion = func() decimal.Decimal { | ||||
| 				if basicPooled.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.PooledBasic.Fee.Div(basicPooled) | ||||
| 			}() | ||||
| 			meter.PooledAdjust.Proportion = func() decimal.Decimal { | ||||
| 				if adjustPooled.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.PooledAdjust.Fee.Div(adjustPooled) | ||||
| 			}() | ||||
| 			meter.PooledLoss.Proportion = func() decimal.Decimal { | ||||
| 				if lossPooled.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.PooledLoss.Fee.Div(lossPooled) | ||||
| 			}() | ||||
| 			meter.PooledPublic.Proportion = func() decimal.Decimal { | ||||
| 				if publicPooled.Equal(decimal.Zero) { | ||||
| 					return decimal.Zero | ||||
| 				} | ||||
| 				return meter.PooledPublic.Fee.Div(publicPooled) | ||||
| 			}() | ||||
|  | ||||
| 			var OverallProportion decimal.Decimal | ||||
| 			if summary.Overall.Amount == decimal.Zero { | ||||
| 				OverallProportion = decimal.Zero | ||||
| 			} else { | ||||
| 				OverallProportion = decimal.NewFromFloat(overall.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64()) | ||||
| 			} | ||||
|  | ||||
| 			var CriticalProportion decimal.Decimal | ||||
| 			if summary.Critical.Amount == decimal.Zero { | ||||
| 				CriticalProportion = decimal.Zero | ||||
| 			} else { | ||||
| 				CriticalProportion = decimal.NewFromFloat(critical.Amount.InexactFloat64() / summary.Critical.Amount.InexactFloat64()) | ||||
| 			} | ||||
|  | ||||
| 			var PeakProportion decimal.Decimal | ||||
| 			if summary.Peak.Amount == decimal.Zero { | ||||
| 				PeakProportion = decimal.Zero | ||||
| 			} else { | ||||
| 				PeakProportion = decimal.NewFromFloat(peak.Amount.InexactFloat64() / summary.Peak.Amount.InexactFloat64()) | ||||
| 			} | ||||
|  | ||||
| 			var FlatProportion decimal.Decimal | ||||
| 			if summary.Flat.Amount == decimal.Zero { | ||||
| 				FlatProportion = decimal.Zero | ||||
| 			} else { | ||||
| 				FlatProportion = decimal.NewFromFloat(flat.Amount.InexactFloat64() / summary.Flat.Amount.InexactFloat64()) | ||||
| 			} | ||||
|  | ||||
| 			var ValleyProportion decimal.Decimal | ||||
| 			if summary.Valley.Amount == decimal.Zero { | ||||
| 				ValleyProportion = decimal.Zero | ||||
| 			} else { | ||||
| 				ValleyProportion = decimal.NewFromFloat(valley.Amount.InexactFloat64() / summary.Valley.Amount.InexactFloat64()) | ||||
| 			} | ||||
|  | ||||
| 			tenementCharge := calculate.TenementCharge{ | ||||
| 				Tenement: tCode, | ||||
| 				Overall: model.ConsumptionUnit{ | ||||
| 					Price:      summary.Overall.Price, | ||||
| 					Proportion: OverallProportion, | ||||
| 				}, | ||||
| 				Critical: model.ConsumptionUnit{ | ||||
| 					Price:      summary.Critical.Price, | ||||
| 					Proportion: CriticalProportion, | ||||
| 				}, | ||||
| 				Peak: model.ConsumptionUnit{ | ||||
| 					Price:      summary.Overall.Price, | ||||
| 					Proportion: PeakProportion, | ||||
| 				}, | ||||
| 				Flat: model.ConsumptionUnit{ | ||||
| 					Price:      summary.Overall.Price, | ||||
| 					Proportion: FlatProportion, | ||||
| 				}, | ||||
| 				Valley: model.ConsumptionUnit{ | ||||
| 					Price:      summary.Overall.Price, | ||||
| 					Proportion: ValleyProportion, | ||||
| 				}, | ||||
| 				BasicFee:     basicPooled, | ||||
| 				AdjustFee:    adjustPooled, | ||||
| 				LossPooled:   lossPooled, | ||||
| 				PublicPooled: publicPooled, | ||||
| 				FinalCharges: decimal.NewFromFloat( | ||||
| 					overall.Fee.InexactFloat64() + basicPooled.InexactFloat64() + | ||||
| 						adjustPooled.InexactFloat64() + lossPooled.InexactFloat64() + | ||||
| 						publicPooled.InexactFloat64()), | ||||
| 				Submeters: nil, | ||||
| 				Poolings:  nil, | ||||
| 			} | ||||
|  | ||||
| 			tc = append(tc, tenementCharge) | ||||
| 		} | ||||
| 	} | ||||
| 	return tc | ||||
| } | ||||
|   | ||||
							
								
								
									
										141
									
								
								service/calculate/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								service/calculate/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/shopspring/decimal" | ||||
| ) | ||||
|  | ||||
| // 计算两个读书之间的有功(总)电量 | ||||
| func ComputeOverall(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Overall.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Overall.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("表计 {%s} 有功(总)开始读数 {%x} 大于结束读数 {%x}", startReading.Meter, start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
|  | ||||
| 	var summaryAmount float64 | ||||
| 	if summary.Overall.Amount == decimal.Zero { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Overall.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Overall.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Overall.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| //计算两个读书之间的尖峰电量 | ||||
| func ComputeCritical(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Critical.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Critical.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("尖峰开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Critical.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Critical.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Critical.Amount.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Critical.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // 计算两个读数之间的峰电量 | ||||
| func ComputePeak(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Peak.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := startReading.Peak.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("峰开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Peak.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Peak.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Peak.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Peak.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| //计算两个读数之间的平电量 | ||||
| func ComputeFlat(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Flat.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Flat.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("平开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Flat.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Flat.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Flat.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Flat.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| //计算两个读数之间的谷电量 | ||||
| func ComputeValley(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) { | ||||
| 	start := startReading.Valley.InexactFloat64() * startReading.Ratio.InexactFloat64() | ||||
| 	end := endReading.Valley.InexactFloat64() * endReading.Ratio.InexactFloat64() | ||||
|  | ||||
| 	if start > end { | ||||
| 		return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("谷开始读数 {%x} 大于结束读数 {%x}", start, end)) | ||||
| 	} | ||||
|  | ||||
| 	amount := end - start | ||||
|  | ||||
| 	var summaryAmount float64 | ||||
|  | ||||
| 	if summary.Valley.Amount.Equal(decimal.Zero) { | ||||
| 		summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64() | ||||
| 	} else { | ||||
| 		summaryAmount = summary.Valley.Amount.InexactFloat64() | ||||
| 	} | ||||
|  | ||||
| 	return model.ConsumptionUnit{ | ||||
| 		Amount:     decimal.NewFromFloat(amount), | ||||
| 		Fee:        decimal.NewFromFloat(amount * summary.Valley.Price.InexactFloat64()), | ||||
| 		Price:      decimal.NewFromFloat(summary.Valley.Price.InexactFloat64()), | ||||
| 		Proportion: decimal.NewFromFloat(amount / summaryAmount), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										168
									
								
								service/calculate/wattCost.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								service/calculate/wattCost.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| package calculate | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/model/calculate" | ||||
| 	"electricity_bill_calc/repository" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| func MainCalculateProcess(rid string) { | ||||
| 	report, err := repository.ReportRepository.GetReportIndex(rid) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("1", err.Error()+"指定报表不存在") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	reportSummary, err := repository.ReportRepository.RetrieveReportSummary(rid) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("2", err.Error()+"指定报表的基本电量电费数据不存在") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	summary := calculate.FromReportSummary(reportSummary, report) | ||||
|  | ||||
| 	periodStart := report.Period.SafeLower() | ||||
| 	periodEnd := report.Period.SafeUpper() | ||||
|  | ||||
| 	meterDetails, err := repository.MeterRepository.AllUsedMetersInReport(report.Id) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("3", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	meterRelations, err := repository.CalculateRepository.GetAllPoolingMeterRelations(report.Park, periodStart.Time) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("4", err) | ||||
| 		return | ||||
| 	} | ||||
| 	_, err = CheckMeterArea(report, meterDetails) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("5", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 寻找每一个商户的所有表计读数,然后对分配到各个商户的表计读数进行初步的计算. | ||||
| 	tenementReports, err := TenementMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("6", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 取得所有公摊表计的读数,以及公摊表计对应的分摊表计 | ||||
| 	poolingMetersReports, err := PooledMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("7", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 获取所有的物业表计,然后对所有的物业表计电量进行计算。 | ||||
| 	parkMetersReports, err := MetersParkCalculate(*report, periodStart.Time, periodEnd.Time, meterDetails, summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("8", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 计算所有表计的总电量 | ||||
| 	parkTotal := TotalConsumptionCalculate(tenementReports, summary) | ||||
|  | ||||
| 	// 计算线损以及调整线损 | ||||
| 	err = LossCalculate(report, &parkMetersReports, &parkTotal, &summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("9", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。 | ||||
| 	_, err = EnabledAreaCalculate(&tenementReports, &summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("10", err) | ||||
| 		return | ||||
| 	} | ||||
| 	err = CalculatePrices(&summary) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("11", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	//=========================================================================== | ||||
| 	// 计算基本电费分摊、调整电费分摊、电费摊薄单价。 | ||||
| 	err = CalculatePrices(&summary) | ||||
| 	// 收集目前所有已经处理的表计,统一对其进行摊薄计算。 | ||||
| 	meters, err := CollectMeters(tenementReports, poolingMetersReports, parkMetersReports) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("12", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 计算商户的合计电费信息,并归总与商户相关联的表计记录 | ||||
| 	tenementCharges := TenementChargeCalculate(tenementReports, summary, meters) | ||||
|  | ||||
| 	// 根据核算报表中设置的摊薄内容,逐个表计进行计算 | ||||
| 	err = CalculateBasicPooling(report, &summary, &meters) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("13", err) | ||||
| 		return | ||||
| 	} | ||||
| 	err = CalculateAdjustPooling(*report, summary, meters) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("14", err) | ||||
| 		return | ||||
| 	} | ||||
| 	err = CalculateLossPooling(*report, summary, meters) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("15", err) | ||||
| 		return | ||||
| 	} | ||||
| 	// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。 | ||||
| 	_, err = CalculateTenementConsumptions(meters) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("16", err) | ||||
| 		return | ||||
| 	} | ||||
| 	err = CalculateTenementPoolings(*report, summary, meters, meterRelations) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("17", err) | ||||
| 		return | ||||
| 	} | ||||
| 	// 计算商户的合计电费信息,并归总与商户相关联的表计记录 | ||||
| 	tenementCharges = TenementChargeCalculate(tenementReports, summary, meters) | ||||
|  | ||||
| 	// 从此处开始向数据库保存全部计算结果。 | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	tx, _ := global.DB.Begin(ctx) | ||||
| 	err = repository.CalculateRepository.ClearReportContent(tx, report.Id) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		fmt.Println("18", err) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	err = SaveSummary(tx, summary) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		fmt.Println("19", err) | ||||
|  | ||||
| 	} | ||||
| 	err = SavePublics(tx, *report, meters) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		fmt.Println("20", err) | ||||
|  | ||||
| 	} | ||||
| 	err = SavePoolings(tx, *report, meters, meterRelations) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		fmt.Println("21", err) | ||||
|  | ||||
| 	} | ||||
| 	err = SaveTenements(tx, *report, tenementReports, tenementCharges) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback(ctx) | ||||
| 		fmt.Println("22", err) | ||||
|  | ||||
| 	} | ||||
| 	tx.Commit(ctx) | ||||
|  | ||||
| } | ||||
| @@ -1,479 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/excel" | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	mapset "github.com/deckarep/golang-set/v2" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _EndUserService struct { | ||||
| 	l *zap.Logger | ||||
| } | ||||
|  | ||||
| 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) { | ||||
| 	var ( | ||||
| 		conditions = make([]string, 0) | ||||
| 		endUsers   = make([]model.EndUserDetail, 0) | ||||
| 		cond       = global.DB.NewSelect().Model(&endUsers) | ||||
| 	) | ||||
| 	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) | ||||
| 	} | ||||
| 	if cachedTotal, err := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 && err == nil { | ||||
| 		if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", conditions...); cachedEndUsers != nil { | ||||
| 			return *cachedEndUsers, cachedTotal, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	startItem := (page - 1) * config.ServiceSettings.ItemsPageSize | ||||
| 	total, err := cond.Limit(config.ServiceSettings.ItemsPageSize). | ||||
| 		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) { | ||||
| 	if cachedEndUsers, _ := cache.RetreiveSearch[[]model.EndUserDetail]("end_user_detail", "report", reportId); cachedEndUsers != nil { | ||||
| 		return *cachedEndUsers, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	users := make([]model.EndUserDetail, 0) | ||||
| 	err := global.DB.NewSelect().Model(&users). | ||||
| 		Where("report_id = ?", reportId). | ||||
| 		Order("seq asc", "meter_04kv_id asc"). | ||||
| 		Scan(ctx) | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| func (_EndUserService) FetchSpecificEndUserRecord(reportId, parkId, meterId string) (*model.EndUserDetail, error) { | ||||
| 	if cachedEndUser, _ := cache.RetreiveEntity[model.EndUserDetail]("end_user_detail", fmt.Sprintf("%s_%s_%s", reportId, parkId, meterId)); cachedEndUser != nil { | ||||
| 		return cachedEndUser, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	record := new(model.EndUserDetail) | ||||
| 	err := global.DB.NewSelect().Model(record). | ||||
| 		Where("report_id = ?", reportId). | ||||
| 		Where("park_id = ?", parkId). | ||||
| 		Where("meter_04kv_id = ?", 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 | ||||
| } | ||||
|  | ||||
| func (_EndUserService) UpdateEndUserRegisterRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail) (err error) { | ||||
| 	record.CalculatePeriod() | ||||
| 	updateColumns := []string{ | ||||
| 		"current_period_overall", | ||||
| 		"adjust_overall", | ||||
| 		"current_period_critical", | ||||
| 		"current_period_peak", | ||||
| 		"current_period_flat", | ||||
| 		"current_period_valley", | ||||
| 		"adjust_critical", | ||||
| 		"adjust_peak", | ||||
| 		"adjust_flat", | ||||
| 		"adjust_valley", | ||||
| 		"overall", | ||||
| 		"critical", | ||||
| 		"peak", | ||||
| 		"flat", | ||||
| 		"valley", | ||||
| 	} | ||||
| 	if record.Initialize { | ||||
| 		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 | ||||
| } | ||||
|  | ||||
| func (_EndUserService) newVirtualExcelAnalysisError(err error) *excel.ExcelAnalysisError { | ||||
| 	return &excel.ExcelAnalysisError{Col: -1, Row: -1, Err: excel.AnalysisError{Err: err}} | ||||
| } | ||||
|  | ||||
| func (es _EndUserService) BatchImportNonPVRegister(reportId string, file io.Reader) *exceptions.BatchError { | ||||
| 	ctx, cancel := global.TimeoutContext(120) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	errs := exceptions.NewBatchError() | ||||
| 	users, err := es.AllEndUserRecord(reportId) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		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( | ||||
| 		users, | ||||
| 		func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail { | ||||
| 			acc[elem.MeterId] = elem | ||||
| 			return acc | ||||
| 		}, | ||||
| 		make(map[string]model.EndUserDetail, 0), | ||||
| 	) | ||||
| 	analyzer, err := excel.NewEndUserNonPVExcelAnalyzer(file) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		return errs | ||||
| 	} | ||||
| 	imports, excelErrs := analyzer.Analysis(*new(model.EndUserImport)) | ||||
| 	if len(excelErrs) > 0 { | ||||
| 		for _, e := range excelErrs { | ||||
| 			errs.AddError(e) | ||||
| 		} | ||||
| 		return errs | ||||
| 	} | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		return errs | ||||
| 	} | ||||
|  | ||||
| 	for _, im := range imports { | ||||
| 		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.AdjustOverall = im.AdjustOverall | ||||
| 			elem.CurrentPeriodCritical = decimal.Zero | ||||
| 			elem.CurrentPeriodPeak = decimal.Zero | ||||
| 			elem.CurrentPeriodValley = decimal.Zero | ||||
| 			elem.CurrentPeriodFlat = elem.CurrentPeriodOverall.Sub(elem.CurrentPeriodCritical).Sub(elem.CurrentPeriodPeak).Sub(elem.CurrentPeriodValley) | ||||
| 			elem.AdjustCritical = decimal.Zero | ||||
| 			elem.AdjustPeak = decimal.Zero | ||||
| 			elem.AdjustValley = decimal.Zero | ||||
| 			elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley) | ||||
| 			err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem) | ||||
| 			if err != nil { | ||||
| 				errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 			} | ||||
| 		} else { | ||||
| 			errs.AddError(exceptions.NewNotFoundError(fmt.Sprintf("表计 %s 未找到", im.MeterId))) | ||||
| 		} | ||||
| 	} | ||||
| 	if errs.Len() > 0 { | ||||
| 		tx.Rollback() | ||||
| 		return errs | ||||
| 	} | ||||
|  | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 	} | ||||
| 	cache.AbolishRelation("end_user_detail") | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| func (es _EndUserService) BatchImportPVRegister(reportId string, file io.Reader) *exceptions.BatchError { | ||||
| 	ctx, cancel := global.TimeoutContext(120) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	errs := exceptions.NewBatchError() | ||||
| 	users, err := es.AllEndUserRecord(reportId) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		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( | ||||
| 		users, | ||||
| 		func(acc map[string]model.EndUserDetail, elem model.EndUserDetail, index int) map[string]model.EndUserDetail { | ||||
| 			acc[elem.MeterId] = elem | ||||
| 			return acc | ||||
| 		}, | ||||
| 		make(map[string]model.EndUserDetail, 0), | ||||
| 	) | ||||
| 	analyzer, err := excel.NewEndUserPVExcelAnalyzer(file) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		return errs | ||||
| 	} | ||||
| 	imports, excelErrs := analyzer.Analysis(*new(model.EndUserImport)) | ||||
| 	if len(excelErrs) > 0 { | ||||
| 		for _, e := range excelErrs { | ||||
| 			errs.AddError(e) | ||||
| 		} | ||||
| 		return errs | ||||
| 	} | ||||
|  | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 		return errs | ||||
| 	} | ||||
|  | ||||
| 	for _, im := range imports { | ||||
| 		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.AdjustOverall = im.AdjustOverall | ||||
| 			elem.CurrentPeriodCritical = im.CurrentPeriodCritical.Decimal | ||||
| 			elem.CurrentPeriodPeak = im.CurrentPeriodPeak.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.AdjustPeak = im.AdjustPeak.Decimal | ||||
| 			elem.AdjustValley = im.AdjustValley.Decimal | ||||
| 			elem.AdjustFlat = elem.AdjustOverall.Sub(elem.AdjustCritical).Sub(elem.AdjustPeak).Sub(elem.AdjustValley) | ||||
| 			err := es.UpdateEndUserRegisterRecord(&tx, &ctx, elem) | ||||
| 			if err != nil { | ||||
| 				errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 			} | ||||
| 		} else { | ||||
| 			errs.AddError(es.newVirtualExcelAnalysisError(exceptions.NewNotFoundError(fmt.Sprintf("表计 %s 未找到", im.MeterId)))) | ||||
| 		} | ||||
| 	} | ||||
| 	if errs.Len() > 0 { | ||||
| 		tx.Rollback() | ||||
| 		return errs | ||||
| 	} | ||||
|  | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		errs.AddError(es.newVirtualExcelAnalysisError(err)) | ||||
| 	} | ||||
| 	cache.AbolishRelation("end_user_detail") | ||||
| 	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 | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,295 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
|  | ||||
| 	mapset "github.com/deckarep/golang-set/v2" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/shopspring/decimal" | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _MaintenanceFeeService struct { | ||||
| 	l *zap.Logger | ||||
| } | ||||
|  | ||||
| var MaintenanceFeeService = _MaintenanceFeeService{ | ||||
| 	l: logger.Named("Service", "maintenance"), | ||||
| } | ||||
|  | ||||
| func (_MaintenanceFeeService) ListMaintenanceFees(pid []string, period string, requestPage int) ([]model.MaintenanceFee, int64, error) { | ||||
| 	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 { | ||||
| 		cond = cond.Where("park_id in (?)", bun.In(pid)) | ||||
| 	} else { | ||||
| 		return make([]model.MaintenanceFee, 0), 0, exceptions.NewIllegalArgumentsError("必须给定所要请求的至少一个园区", "park_id") | ||||
| 	} | ||||
| 	if len(period) > 0 { | ||||
| 		cond = cond.Where("period = ?", period) | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 		return make([]model.MaintenanceFee, 0), 0, fmt.Errorf("附加费查询出现错误,%w", err) | ||||
| 	} | ||||
| 	relations := lo.Map(fees, func(f model.MaintenanceFee, _ int) string { | ||||
| 		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 { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	fee.Id = uuid.New().String() | ||||
| 	fee.Enabled = true | ||||
| 	_, err := global.DB.NewInsert().Model(&fee).Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.AbolishRelation("maintenance_fee") | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_MaintenanceFeeService) ModifyMaintenanceFee(fee model.MaintenanceFee) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	res, err := global.DB.NewUpdate().Model(&fee). | ||||
| 		WherePK(). | ||||
| 		Column("fee", "memo"). | ||||
| 		Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		if rows, _ := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation("maintenance_fee") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fee.Id)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_MaintenanceFeeService) ChangeMaintenanceFeeState(fid string, state bool) error { | ||||
| 	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 rows, err := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation("maintenance_fee") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_MaintenanceFeeService) DeleteMaintenanceFee(fid string) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	res, err := global.DB.NewDelete().Model((*model.MaintenanceFee)(nil)). | ||||
| 		Where("id = ?", fid). | ||||
| 		Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		if rows, err := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到匹配的附加费记录。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation("maintenance_fee") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_MaintenanceFeeService) EnsureFeeBelongs(uid, mid string) (bool, error) { | ||||
| 	if has, _ := cache.CheckExists("maintenance_fee", mid, uid); has { | ||||
| 		return true, nil | ||||
| 	} | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	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 { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	exists := lo.Reduce(parks, func(acc bool, elem model.Park, _ int) bool { | ||||
| 		for _, e := range elem.MaintenanceFees { | ||||
| 			if e.Id == mid { | ||||
| 				return acc || true | ||||
| 			} | ||||
| 		} | ||||
| 		return acc || false | ||||
| 	}, false) | ||||
| 	if !exists { | ||||
| 		return false, exceptions.NewNotFoundError("指定附加费所属园区未找到。") | ||||
| 	} | ||||
| 	cache.CacheExists([]string{fmt.Sprintf("maintenance_fee:%s", mid), "maintenance_fee", "park"}, "maintenance_fee", mid, uid) | ||||
| 	return exists, 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 | ||||
| } | ||||
| @@ -1,231 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/excel" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
|  | ||||
| 	mapset "github.com/deckarep/golang-set/v2" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _Meter04kVService struct { | ||||
| 	l *zap.Logger | ||||
| } | ||||
|  | ||||
| var Meter04kVService = _Meter04kVService{ | ||||
| 	l: logger.Named("Service", "Meter04KV"), | ||||
| } | ||||
|  | ||||
| func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]model.Meter04KV, int64, error) { | ||||
| 	var ( | ||||
| 		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)) | ||||
| 	if len(keyword) > 0 { | ||||
| 		keywordCond := "%" + keyword + "%" | ||||
| 		cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { | ||||
| 			return q.Where("address like ?", keywordCond). | ||||
| 				WhereOr("code like ?", keywordCond). | ||||
| 				WhereOr("customer_name like ?", keywordCond). | ||||
| 				WhereOr("contact_name like ?", keywordCond). | ||||
| 				WhereOr("contact_phone like ?", keywordCond) | ||||
| 		}) | ||||
| 		condition = append(condition, keyword) | ||||
| 	} | ||||
| 	if cachedTotal, err := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 && err == nil { | ||||
| 		if cachedMeters, _ := cache.RetreiveSearch[[]model.Meter04KV]("meter_04kv", condition...); cachedMeters != nil { | ||||
| 			return *cachedMeters, cachedTotal, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	startItem := (page - 1) * config.ServiceSettings.ItemsPageSize | ||||
| 	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) { | ||||
| 	if cachedMeter, _ := cache.RetreiveEntity[model.Meter04KV]("meter_04kv", fmt.Sprintf("%s_%s", park, code)); cachedMeter != nil { | ||||
| 		return cachedMeter, nil | ||||
| 	} | ||||
| 	var meter = new(model.Meter04KV) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	err := global.DB.NewSelect().Model(meter). | ||||
| 		Where("code = ?", code). | ||||
| 		Where("park_id = ?", park). | ||||
| 		Scan(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cache.CacheEntity(meter, []string{fmt.Sprintf("meter_04kv:%s:%s", park, code), "park"}, "meter_04kv", fmt.Sprintf("%s_%s", park, code)) | ||||
| 	return meter, nil | ||||
| } | ||||
|  | ||||
| func (_Meter04kVService) insertNewMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error { | ||||
| 	_, err := tx.NewInsert().Model(&meter).Exec(*ctx) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 	} | ||||
| 	cache.AbolishRelation("meter_04kv") | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (_Meter04kVService) updateMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error { | ||||
| 	_, err := tx.NewUpdate().Model(&meter). | ||||
| 		Where("code = ?", meter.Code). | ||||
| 		Where("park_id = ?", meter.ParkId). | ||||
| 		Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled"). | ||||
| 		Exec(*ctx) | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 	} | ||||
| 	cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = m.insertNewMeter(&tx, &ctx, meter) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.AbolishRelation("meter_04kv") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = m.updateMeter(&tx, &ctx, *meter) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_Meter04kVService) DuplicateMeterCodeValidate(meters []model.Meter04KV) []excel.ExcelAnalysisError { | ||||
| 	errs := make([]excel.ExcelAnalysisError, 0) | ||||
| 	for i := 0; i < len(meters); i++ { | ||||
| 		for j := i + 1; j < len(meters); j++ { | ||||
| 			if meters[j].Code == meters[i].Code { | ||||
| 				errs = append(errs, excel.ExcelAnalysisError{Row: j + 1, Col: 0, Err: excel.AnalysisError{Err: fmt.Errorf("第 %d 行表计表号与第 %d 行表计表号重复!", j+1, i+1)}}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| func (m _Meter04kVService) BatchCreateMeter(meters []model.Meter04KV) error { | ||||
| 	parkIds := lo.Reduce(meters, func(acc mapset.Set[string], elem model.Meter04KV, index int) mapset.Set[string] { | ||||
| 		acc.Add(elem.ParkId) | ||||
| 		return acc | ||||
| 	}, mapset.NewSet[string]()) | ||||
| 	if parkIds.Cardinality() > 1 { | ||||
| 		return fmt.Errorf("一次只能向同一个园区中添加0.4kV表计。") | ||||
| 	} | ||||
| 	parkId, _ := parkIds.Pop() | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext(120) | ||||
| 	defer cancel() | ||||
|  | ||||
| 	allMeterCodes := make([]string, 0) | ||||
| 	err := global.DB.NewSelect().Model((*model.Meter04KV)(nil)). | ||||
| 		Where("park_id = ?", parkId). | ||||
| 		Column("code"). | ||||
| 		Scan(ctx, &allMeterCodes) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	meterCodes := mapset.NewSet(allMeterCodes...) | ||||
|  | ||||
| 	tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		updates = make([]model.Meter04KV, 0) | ||||
| 		inserts = make([]model.Meter04KV, 0) | ||||
| 	) | ||||
| 	for _, meter := range meters { | ||||
| 		if meterCodes.Contains(meter.Code) { | ||||
| 			updates = append(updates, meter) | ||||
| 		} else { | ||||
| 			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 { | ||||
| 			tx.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	if len(inserts) > 0 { | ||||
| 		_, err = tx.NewInsert().Model(&inserts).Exec(ctx) | ||||
| 		if err != nil { | ||||
| 			tx.Rollback() | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.AbolishRelation("meter_04kv") | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										171
									
								
								service/park.go
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								service/park.go
									
									
									
									
									
								
							| @@ -1,171 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _ParkService struct { | ||||
| 	l *zap.Logger | ||||
| } | ||||
|  | ||||
| var ParkService = _ParkService{ | ||||
| 	l: logger.Named("Service", "Park"), | ||||
| } | ||||
|  | ||||
| func (_ParkService) SaveNewPark(park model.Park) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	_, err := global.DB.NewInsert().Model(&park).Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.AbolishRelation("park") | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_ParkService) UpdateParkInfo(park *model.Park) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	res, err := global.DB.NewUpdate().Model(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 rows, _ := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到符合条件的园区。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation(fmt.Sprintf("park:%s", park.Id)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_ParkService) ChangeParkState(uid, pid string, state bool) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	res, err := global.DB.NewUpdate().Model((*model.Park)(nil)). | ||||
| 		Where("id = ?", pid). | ||||
| 		Where("user_id = ?", uid). | ||||
| 		Set("enabled = ?", state). | ||||
| 		Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		if rows, _ := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到符合条件的园区。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation(fmt.Sprintf("park:%s", pid)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_ParkService) DeletePark(uid, pid string) error { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	res, err := global.DB.NewDelete().Model((*model.Park)(nil)). | ||||
| 		Where("id = ?", pid). | ||||
| 		Where("user_id = ?", uid). | ||||
| 		Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		if rows, _ := res.RowsAffected(); rows == 0 { | ||||
| 			return exceptions.NewNotFoundError("未能找到符合条件的园区。") | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	cache.AbolishRelation("park") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("park:%s", pid)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (_ParkService) ListAllParkBelongsTo(uid, keyword string) ([]model.Park, error) { | ||||
| 	if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid, keyword); parks != nil { | ||||
| 		return *parks, nil | ||||
| 	} | ||||
| 	parks := make([]model.Park, 0) | ||||
| 	cond := global.DB.NewSelect().Model(&parks). | ||||
| 		Where("user_id = ?", uid) | ||||
| 	if len(keyword) > 0 { | ||||
| 		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 { | ||||
| 		return make([]model.Park, 0), err | ||||
| 	} | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| func (_ParkService) FetchParkDetail(pid string) (*model.Park, error) { | ||||
| 	if park, _ := cache.RetreiveEntity[model.Park]("park", pid); park != nil { | ||||
| 		return park, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	var park = new(model.Park) | ||||
| 	err := global.DB.NewSelect().Model(park). | ||||
| 		Where("id = ?", pid). | ||||
| 		Scan(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, exceptions.NewNotFoundErrorFromError("未找到符合条件的园区记录。", err) | ||||
| 	} | ||||
| 	cache.CacheEntity(*park, []string{fmt.Sprintf("park:%s", pid)}, "park", pid) | ||||
| 	return park, nil | ||||
| } | ||||
|  | ||||
| func (_ParkService) EnsurePark(uid, pid string) (bool, error) { | ||||
| 	if has, _ := cache.CheckExists("park", pid, uid); has { | ||||
| 		return has, nil | ||||
| 	} | ||||
| 	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 { | ||||
| 		cache.CacheExists([]string{fmt.Sprintf("park:%s", pid)}, "park", pid, uid) | ||||
| 	} | ||||
| 	return has, err | ||||
| } | ||||
|  | ||||
| func (_ParkService) AllParkIds(uid string) ([]string, error) { | ||||
| 	if ids, _ := cache.RetreiveSearch[[]string]("park", "belong", uid); ids != nil { | ||||
| 		return *ids, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	var ids = make([]string, 0) | ||||
| 	err := global.DB.NewSelect().Model((*model.Park)(nil)). | ||||
| 		Where("user_id = ?", uid). | ||||
| 		Column("id"). | ||||
| 		Scan(ctx, &ids) | ||||
| 	if err != nil { | ||||
| 		return make([]string, 0), err | ||||
| 	} | ||||
| 	cache.CacheSearch(ids, []string{"park"}, "park", "belong", uid) | ||||
| 	return ids, nil | ||||
| } | ||||
| @@ -1,70 +0,0 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/samber/lo" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _RegionService struct { | ||||
| 	l *zap.Logger | ||||
| } | ||||
|  | ||||
| var RegionService = _RegionService{ | ||||
| 	l: logger.Named("Service", "Region"), | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 		return *regions, nil | ||||
| 	} | ||||
| 	regions := make([]model.Region, 0) | ||||
| 	err := global.DB.NewSelect().Model(®ions).Where("parent = ?", parent).Order("code asc").Scan(ctx) | ||||
| 	if err != nil { | ||||
| 		return make([]model.Region, 0), err | ||||
| 	} | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| func (r _RegionService) FetchAllParentRegions(code string) ([]model.Region, error) { | ||||
| 	regions := make([]model.Region, 0) | ||||
| 	region, err := r.fetchRegion(code) | ||||
| 	if err != nil { | ||||
| 		return regions, err | ||||
| 	} | ||||
| 	regions = append(regions, *region) | ||||
| 	for region.Level > 1 { | ||||
| 		region, err = r.fetchRegion(region.Parent) | ||||
| 		if err != nil { | ||||
| 			return make([]model.Region, 0), nil | ||||
| 		} | ||||
| 		regions = append(regions, *region) | ||||
| 	} | ||||
| 	return regions, nil | ||||
| } | ||||
|  | ||||
| func (_RegionService) fetchRegion(code string) (*model.Region, error) { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	if cachedRegion, _ := cache.RetreiveSearch[model.Region]("region", code); cachedRegion != nil { | ||||
| 		return cachedRegion, nil | ||||
| 	} | ||||
| 	region := new(model.Region) | ||||
| 	err := global.DB.NewSelect().Model(region).Where("code = ?", code).Scan(ctx) | ||||
| 	if err != nil { | ||||
| 		relationName := fmt.Sprintf("region:%s", code) | ||||
| 		cache.CacheSearch(region, []string{relationName}, "region", code) | ||||
| 	} | ||||
| 	return region, err | ||||
| } | ||||
| @@ -1,94 +1,74 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
|  | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	"github.com/georgysavva/scany/v2/pgxscan" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _StatisticsService struct { | ||||
| 	l *zap.Logger | ||||
| 	l  *zap.Logger | ||||
| 	ss goqu.DialectWrapper | ||||
| } | ||||
|  | ||||
| var StatisticsService = _StatisticsService{ | ||||
| 	l: logger.Named("Service", "Stat"), | ||||
| 	logger.Named("Service", "Stat"), | ||||
| 	goqu.Dialect("postgres"), | ||||
| } | ||||
|  | ||||
| func (_StatisticsService) EnabledEnterprises() (int64, error) { | ||||
| 	if cachedCount, err := cache.RetreiveCount("enabled_ent"); cachedCount != -1 && err == nil { | ||||
| 		return cachedCount, nil | ||||
| 	} | ||||
| //用于统计企业用户数量 | ||||
| func (ss _StatisticsService) EnabledEnterprises() (int64, error) { | ||||
| 	ss.l.Info("开始统计企业数量。") | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	c, err := global.DB.NewSelect().Model((*model.User)(nil)). | ||||
| 		Where("type = ?", model.USER_TYPE_ENT). | ||||
| 		Where("enabled = ?", true). | ||||
| 		Count(ctx) | ||||
| 	if err == nil { | ||||
| 		cache.CacheCount([]string{"user"}, "enabled_ent", int64(c)) | ||||
| 	} | ||||
| 	return int64(c), err | ||||
| } | ||||
| 	UserCountQuery, UserCountQueryArgs, _ := ss.ss. | ||||
| 		From(goqu.T("user")). | ||||
| 		Where(goqu.I("type").Eq(model.USER_TYPE_ENT)). | ||||
| 		Where(goqu.I("enabled").Eq(true)). | ||||
| 		Select(goqu.COUNT("*")).ToSQL() | ||||
|  | ||||
| func (_StatisticsService) EnabledParks(userIds ...string) (int64, error) { | ||||
| 	if cachedParks, err := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 && err == nil { | ||||
| 		return cachedParks, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	query := global.DB.NewSelect().Model((*model.Park)(nil)). | ||||
| 		Where("enabled = ?", true) | ||||
| 	if len(userIds) > 0 { | ||||
| 		query = query.Where("user_id in (?)", bun.In(userIds)) | ||||
| 	} | ||||
| 	c, err := query.Count(ctx) | ||||
| 	if err == nil { | ||||
| 		cache.CacheCount([]string{"user", "park"}, "enabled_parks", int64(c), userIds...) | ||||
| 	} | ||||
| 	return int64(c), err | ||||
| } | ||||
|  | ||||
| func (_StatisticsService) ParksNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) { | ||||
| 	if cachedState, _ := cache.RetreiveSearch[[]model.ParkPeriodStatistics]("park_period_stat", userIds...); cachedState != nil { | ||||
| 		return *cachedState, nil | ||||
| 	} | ||||
| 	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 { | ||||
| 		query = query.Where("park.user_id in (?)", bun.In(userIds)) | ||||
| 	} | ||||
| 	parks := make([]model.ParkPeriodStatistics, 0) | ||||
| 	groupedParks := make(map[string]model.ParkPeriodStatistics, 0) | ||||
| 	err := query.Column("period").Scan(ctx, &parks) | ||||
| 	var c int64 | ||||
| 	err := pgxscan.Get(ctx, global.DB, &c, UserCountQuery, UserCountQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		return make([]model.ParkPeriodStatistics, 0), err | ||||
| 		ss.l.Error("统计企业数量出错", zap.Error(err)) | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	for _, p := range parks { | ||||
| 		if c, ok := groupedParks[p.Id]; ok { | ||||
| 			if c.Period != nil && p.Period != nil && p.Period.After(c.Period.Time) { | ||||
| 				groupedParks[p.Id] = p | ||||
| 			} | ||||
| 			if c.Period == nil && p.Period != nil { | ||||
| 				groupedParks[p.Id] = p | ||||
| 			} | ||||
| 		} else { | ||||
| 			groupedParks[p.Id] = p | ||||
| 		} | ||||
| 	} | ||||
| 	cache.CacheSearch(lo.Values(groupedParks), []string{"user", "park"}, "park_period_stat", userIds...) | ||||
| 	return lo.Values(groupedParks), nil | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| //用于统计园区数量 | ||||
| func (ss _StatisticsService) EnabledParks(userIds ...string) (int64, error) { | ||||
| 	ss.l.Info("开始统计园区数量", zap.Strings("userId", userIds)) | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	ParkCountQuery := ss.ss. | ||||
| 		From(goqu.T("park")). | ||||
| 		Where(goqu.I("enabled").Eq(true)) | ||||
|  | ||||
| 	if len(userIds) > 0 { | ||||
| 		ParkCountQuery = ParkCountQuery.Where(goqu.I("user_id").In(userIds)) | ||||
| 	} | ||||
|  | ||||
| 	ParkCountQuerySql, ParkCountQueryArgs, _ := ParkCountQuery.Select(goqu.COUNT("*")).ToSQL() | ||||
|  | ||||
| 	var c int64 | ||||
| 	err := pgxscan.Get(ctx, global.DB, &c, ParkCountQuerySql, ParkCountQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		ss.l.Error("园区数量统计错误", zap.Error(err)) | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| //用户统计报表 | ||||
| func (ss _StatisticsService) ParkNewestState(userIds ...string) ([]model.ParkPeriodStatistics, error) { | ||||
| 	//TODO: 2023.07.26 报表数据库结构改变,此处逻辑复杂放在最后处理 | ||||
| 	//return nil,errors.New("还未处理逻辑") | ||||
| 	return []model.ParkPeriodStatistics{}, nil | ||||
| } | ||||
|   | ||||
| @@ -80,7 +80,9 @@ func (us _UserService) ProcessEnterpriseUserLogin(username, password string) (*m | ||||
| 		us.log.Error("处理企业用户登录失败。", zap.String("username", username), zap.Error(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	token, _ := uuid.NewRandom() | ||||
|  | ||||
| 	token, _ := uuid.NewRandom() //生成uuid作为会话的token使用 | ||||
|  | ||||
| 	userSession := &model.Session{ | ||||
| 		Uid:       user.Id, | ||||
| 		Name:      user.Username, | ||||
|   | ||||
| @@ -1,160 +1,39 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"electricity_bill_calc/cache" | ||||
| 	"electricity_bill_calc/config" | ||||
| 	"electricity_bill_calc/exceptions" | ||||
| 	"electricity_bill_calc/global" | ||||
| 	"electricity_bill_calc/logger" | ||||
| 	"electricity_bill_calc/model" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/uptrace/bun" | ||||
| 	"github.com/doug-martin/goqu/v9" | ||||
| 	"github.com/georgysavva/scany/v2/pgxscan" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| type _WithdrawService struct { | ||||
| 	l *zap.Logger | ||||
| 	log *zap.Logger | ||||
| 	ds goqu.DialectWrapper | ||||
| } | ||||
|  | ||||
| var WithdrawService = _WithdrawService{ | ||||
| 	l: logger.Named("Service", "Withdraw"), | ||||
| 	logger.Named("Service", "Withdraw"), | ||||
| 	goqu.Dialect("postgres"), | ||||
| } | ||||
|  | ||||
| func (_WithdrawService) ApplyWithdraw(reportId string) (bool, error) { | ||||
| func (wd _WithdrawService) AuditWaits() (int64, error) { | ||||
| 	wd.log.Info("获取当前系统中待审核的内容数量。") | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	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) | ||||
| 	} | ||||
| 	if !report.Published { | ||||
| 		return false, exceptions.NewImproperOperateError("指定报表尚未发布。") | ||||
| 	} | ||||
| 	var maxPublished time.Time | ||||
| 	err = global.DB.NewSelect().Model((*model.Report)(nil)). | ||||
| 		ColumnExpr("max(period)"). | ||||
| 		Where("park_id = ?", report.ParkId). | ||||
| 		Where("published = ?", true). | ||||
| 		Scan(ctx, &maxPublished) | ||||
| 	CountWithdrawQuery, CountWithdrawQueryArgs, _ := wd.ds. | ||||
| 		From(goqu.T("report")). | ||||
| 		Where(goqu.I("withdraw").Eq(model.REPORT_WITHDRAW_APPLYING)). | ||||
| 		Select(goqu.COUNT("*")).ToSQL() | ||||
|  | ||||
| 	var total int64 | ||||
| 	err := pgxscan.Get(ctx, global.DB, &total, CountWithdrawQuery,CountWithdrawQueryArgs...) | ||||
| 	if err != nil { | ||||
| 		return false, exceptions.NewNotFoundError("未能找到匹配的系列报表。") | ||||
| 		wd.log.Error("获取当前系统中待审核的内容数量出错。") | ||||
| 		return 0,err | ||||
| 	} | ||||
| 	if !report.Period.Equal(maxPublished) { | ||||
| 		return false, exceptions.NewImproperOperateError("申请撤回的报表必须是最新已发布的报表。") | ||||
| 	} | ||||
|  | ||||
| 	report.Withdraw = model.REPORT_WITHDRAW_APPLIED | ||||
| 	report.LastWithdrawAppliedAt = lo.ToPtr(time.Now()) | ||||
| 	_, err = global.DB.NewUpdate().Model(report). | ||||
| 		WherePK(). | ||||
| 		Column("withdraw", "last_withdraw_applied_at"). | ||||
| 		Exec(ctx) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	cache.AbolishRelation("withdraw_stat") | ||||
| 	cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) | ||||
| 	cache.AbolishRelation(fmt.Sprintf("publicity:%s", reportId)) | ||||
| 	return true, nil | ||||
| } | ||||
|  | ||||
| func (_WithdrawService) FetchPagedWithdrawApplies(page int, keyword string) ([]model.JoinedReportForWithdraw, int64, error) { | ||||
| 	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(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) | ||||
| 	} | ||||
| 	if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil { | ||||
| 		if cachedReports, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_user_detail", conditions...); cachedReports != nil { | ||||
| 			return *cachedReports, cachedTotal, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
| 	startItem := (page - 1) * config.ServiceSettings.ItemsPageSize | ||||
| 	total, err := cond.Limit(config.ServiceSettings.ItemsPageSize). | ||||
| 		Offset(startItem). | ||||
| 		ScanAndCount(ctx) | ||||
|  | ||||
| 	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 { | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	var report = new(model.Report) | ||||
| 	err := global.DB.NewSelect().Model(report). | ||||
| 		Where("id = ?", reportId). | ||||
| 		Scan(ctx) | ||||
| 	if err != nil { | ||||
| 		return exceptions.NewNotFoundErrorFromError("指定公示报表未找到。", err) | ||||
| 	} | ||||
| 	report.Withdraw = lo.If(granted, model.REPORT_WITHDRAW_GRANTED).Else(model.REPORT_WITHDRAW_DENIED) | ||||
| 	report.LastWithdrawAuditAt = lo.ToPtr(time.Now()) | ||||
| 	if granted { | ||||
| 		report.Published = false | ||||
| 	} | ||||
| 	_, err = global.DB.NewUpdate().Model(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 | ||||
| } | ||||
|  | ||||
| func (_WithdrawService) AuditWaits() (int64, error) { | ||||
| 	if cachedWaits, err := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 && err == nil { | ||||
| 		return cachedWaits, nil | ||||
| 	} | ||||
| 	ctx, cancel := global.TimeoutContext() | ||||
| 	defer cancel() | ||||
|  | ||||
| 	total, err := global.DB.NewSelect().Model((*model.Report)(nil)). | ||||
| 		Where("withdraw = ?", model.REPORT_WITHDRAW_APPLIED). | ||||
| 		Count(ctx) | ||||
| 	if err == nil { | ||||
| 		cache.CacheCount([]string{"withdraw_stat"}, "withdraw_waits", int64(total)) | ||||
| 	} | ||||
| 	return int64(total), err | ||||
| 	return total,nil | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| Database: | ||||
|   User: electricity | ||||
|   Pass: nLgxPO5s8gK2tR0OL0Q | ||||
|   Host: postgres | ||||
|   Port: 5432 | ||||
|   Host: 39.105.39.8 | ||||
|   Port: 9432 | ||||
|   DB: electricity | ||||
|   MaxIdleConns: 0 | ||||
|   MaxOpenConns: 20 | ||||
| @@ -12,12 +12,16 @@ Server: | ||||
|   ReadTimeout: 60 | ||||
|   WriteTimeout: 60 | ||||
| Redis: | ||||
|   Host: redis | ||||
|   Host: 192.168.88.129 | ||||
|   Port: 6379 | ||||
|   Password: TmFRS0w6BIrAPA1Raj | ||||
|   Password: 123456 | ||||
|   DB: 1 | ||||
| Service: | ||||
|   MaxSessionLife: 2h | ||||
|   ItemsPageSize: 20 | ||||
|   CacheLifeTime: 5m | ||||
|   HostSerial: 5 | ||||
|  | ||||
| BaselineLineLossRatio: | ||||
|   Base: 基准线损率 | ||||
|  | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| package tools | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func DifferenceInMonth(t1, t2 time.Time) int { | ||||
| 	var differYear, differMonth int | ||||
| 	differYear = t1.Year() - t2.Year() | ||||
| 	differMonth = int(t1.Month() - t2.Month()) | ||||
| 	return differYear*12 + differMonth | ||||
| } | ||||
|  | ||||
| func IsNextMonth(origin, t time.Time) bool { | ||||
| 	return DifferenceInMonth(t, origin) == 1 | ||||
| } | ||||
| @@ -1,9 +1,11 @@ | ||||
| package tools | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mozillazg/go-pinyin" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -144,3 +146,26 @@ func NullDecimalToString(d decimal.NullDecimal, precision ...int32) *string { | ||||
| 	} | ||||
| 	return lo.ToPtr(d.Decimal.StringFixedBank(precision[0])) | ||||
| } | ||||
|  | ||||
| // 将sql.NullTime转换为*string | ||||
| func NullTime2PointerString(nullTime sql.NullTime) *string { | ||||
| 	var strPtr *string | ||||
| 	if nullTime.Valid { | ||||
| 		str := nullTime.Time.String() | ||||
| 		strPtr = &str | ||||
| 		return strPtr | ||||
| 	} else { | ||||
| 		strPtr = nil | ||||
| 		return strPtr | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 该方法用于将时间解析为字符串指针 | ||||
| func TimeToStringPtr(t *time.Time) *string { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	timeStr := t.Format("2006-01-02 15:04:05") | ||||
| 	return &timeStr | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								vo/withdraw.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vo/withdraw.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| package vo | ||||
|  | ||||
| //用于接收审核报表的参数 | ||||
| type ReviewWithdraw struct { | ||||
| 	Audit bool `json:"audit"` | ||||
| } | ||||
		Reference in New Issue
	
	Block a user