diff --git a/controller/charge.go b/controller/charge.go deleted file mode 100644 index a2b8d43..0000000 --- a/controller/charge.go +++ /dev/null @@ -1,93 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "net/http" - "strconv" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/shopspring/decimal" -) - -func InitializeChargesController(app *fiber.App) { - app.Get("/charges", security.OPSAuthorize, listAllCharges) - app.Post("/charge", security.OPSAuthorize, recordNewCharge) - app.Put("/charge/:uid/:seq", security.OPSAuthorize, modifyChargeState) -} - -func listAllCharges(c *fiber.Ctx) error { - result := response.NewResult(c) - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - requestKeyword := c.Query("keyword", "") - requestBeginDate := c.Query("begin", "") - requestEndDate := c.Query("end", "") - charges, total, err := service.ChargeService.ListPagedChargeRecord(requestKeyword, requestBeginDate, requestEndDate, requestPage) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, "已获取到符合条件的计费记录。", - response.NewPagedResponse(requestPage, total).ToMap(), - fiber.Map{"records": charges}, - ) -} - -type _NewChargeFormData struct { - UserId string `json:"userId" form:"userId"` - Fee decimal.NullDecimal `json:"fee" form:"fee"` - Discount decimal.NullDecimal `json:"discount" form:"discount"` - Amount decimal.NullDecimal `json:"amount" form:"amount"` - ChargeTo model.Date `json:"chargeTo" form:"chargeTo"` -} - -func recordNewCharge(c *fiber.Ctx) error { - result := response.NewResult(c) - formData := new(_NewChargeFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - currentTime := time.Now() - newRecord := &model.UserCharge{ - UserId: formData.UserId, - Fee: formData.Fee, - Discount: formData.Discount, - Amount: formData.Amount, - Settled: true, - SettledAt: ¤tTime, - ChargeTo: formData.ChargeTo, - } - err := service.ChargeService.CreateChargeRecord(newRecord, true) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Created("指定用户的服务已延期。") -} - -type _StateChangeFormData struct { - Cancelled bool `json:"cancelled"` -} - -func modifyChargeState(c *fiber.Ctx) error { - result := response.NewResult(c) - formData := new(_StateChangeFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - requestUserID := c.Params("uid") - requestChargeSeq, err := strconv.Atoi(c.Params("seq", "-1")) - if err != nil || requestChargeSeq == -1 { - return result.Error(http.StatusNotAcceptable, "参数[记录流水号]解析错误。") - } - err = service.ChargeService.CancelCharge(int64(requestChargeSeq), requestUserID) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定用户服务延期记录状态已经更新。") -} diff --git a/controller/end_user.go b/controller/end_user.go deleted file mode 100644 index 9bb6449..0000000 --- a/controller/end_user.go +++ /dev/null @@ -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}, - ) -} diff --git a/controller/god_mode.go b/controller/god_mode.go deleted file mode 100644 index e431348..0000000 --- a/controller/god_mode.go +++ /dev/null @@ -1,176 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/exceptions" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "net/http" - - "github.com/gofiber/fiber/v2" -) - -func InitializeGodModeController(router *fiber.App) { - gmR := router.Group("/gm") - { - gmR.Delete("/report/:rid/summary", security.SingularityAuthorize, gmResetReportSummary) - gmR.Delete("/report/:rid/maintenance", security.SingularityAuthorize, gmResetReportMaintenance) - gmR.Delete("/report/:rid/meters", security.SingularityAuthorize, gmResetReportEndUserRecord) - gmR.Post("/report/:rid/meters", security.SingularityAuthorize, gmResynchronizeReportEndUserRecord) - gmR.Delete("/report/:rid", security.SingularityAuthorize, gmResetReport) - gmR.Delete("/report/:rid/force", security.SingularityAuthorize, gmDeleteReport) - gmR.Delete("/park/:pid/maintenance/:mid", security.SingularityAuthorize, gmDeleteSpecificMaintenance) - gmR.Delete("/park/:pid/maintenance", security.SingularityAuthorize, gmDeleteAllMaintenance) - gmR.Delete("/park/:pid/meters", security.SingularityAuthorize, gmDeleteAllMeters) - gmR.Delete("/park/:pid/force", security.SingularityAuthorize, gmDeletePark) - gmR.Delete("/enterprise/:uid/force", security.SingularityAuthorize, gmDeleteUser) - } -} - -func gmResetReportSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ClearReportSummary(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的园区总览部分。") - } - return result.Success("指定报表的园区总览已经重置。") -} - -func gmResetReportMaintenance(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ClearReportMaintenances(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的配电维护费部分。") - } - return result.Success("指定报表的配电维护费已经重置。") -} - -func gmResynchronizeReportEndUserRecord(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResynchronizeEndUser(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录基本档案。") - } - return result.Success("指定报表的抄表记录基本档案已经重新同步。") -} - -func gmResetReportEndUserRecord(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResetEndUserRegisterRecords(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表的抄表记录部分。") - } - return result.Success("指定报表的抄表记录已经重置。") -} - -func gmResetReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.ResetReport(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功重置指定报表。") - } - return result.Success("指定报表已经重置。") -} - -func gmDeleteReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - done, err := service.GodModeService.DeleteReport(requestReportId) - if err != nil { - if ipErr, ok := err.(exceptions.ImproperOperateError); ok { - return result.NotAccept(ipErr.Message) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定报表。") - } - return result.Success("指定报表已经删除。") -} - -func gmDeleteSpecificMaintenance(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - requestMaintenanceId := c.Params("mid") - done, err := service.GodModeService.RemoveSpecificMaintenance(requestParkId, requestMaintenanceId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的维护费用记录。") - } - return result.Success("指定维护费用记录已经删除。") -} - -func gmDeleteAllMaintenance(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemoveAllMaintenance(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除全部维护费用记录。") - } - return result.Success("全部维护费用记录已经删除。") -} - -func gmDeleteAllMeters(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemoveAllMeters(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除全部终端表计档案记录。") - } - return result.Success("全部终端表计档案记录已经删除。") -} - -func gmDeletePark(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - done, err := service.GodModeService.RemovePark(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的园区。") - } - return result.Success("指定的园区已经删除。") -} - -func gmDeleteUser(c *fiber.Ctx) error { - result := response.NewResult(c) - requestUserId := c.Params("uid") - done, err := service.GodModeService.DeleteUser(requestUserId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !done { - return result.Error(http.StatusInternalServerError, "未能成功删除指定的用户。") - } - return result.Success("指定的用户及其关联信息已经删除。") -} diff --git a/controller/maintenance_fee.go b/controller/maintenance_fee.go deleted file mode 100644 index 9249ae6..0000000 --- a/controller/maintenance_fee.go +++ /dev/null @@ -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}, - ) -} diff --git a/controller/meter04kv.go b/controller/meter04kv.go deleted file mode 100644 index b3fd7f4..0000000 --- a/controller/meter04kv.go +++ /dev/null @@ -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)}, - ) -} diff --git a/controller/park.go b/controller/park.go deleted file mode 100644 index b7973a0..0000000 --- a/controller/park.go +++ /dev/null @@ -1,185 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "electricity_bill_calc/tools" - "net/http" - - "github.com/gofiber/fiber/v2" - "github.com/google/uuid" - "github.com/jinzhu/copier" - "github.com/shopspring/decimal" -) - -func InitializeParkController(router *fiber.App) { - router.Get("/parks", security.EnterpriseAuthorize, listAllParksUnderSessionUser) - router.Get("/parks/:uid", security.MustAuthenticated, listAllParksUnderSpecificUser) - router.Post("/park", security.EnterpriseAuthorize, createNewPark) - router.Put("/park/:pid", security.EnterpriseAuthorize, modifyPark) - router.Get("/park/:pid", security.EnterpriseAuthorize, fetchParkDetail) - router.Put("/park/:pid/enabled", security.EnterpriseAuthorize, changeParkEnableState) - router.Delete("/park/:pid", security.EnterpriseAuthorize, deleteSpecificPark) -} - -func ensureParkBelongs(c *fiber.Ctx, result *response.Result, requestParkId string) (bool, error) { - userSession, err := _retreiveSession(c) - if err != nil { - return false, result.Unauthorized(err.Error()) - } - sure, err := service.ParkService.EnsurePark(userSession.Uid, requestParkId) - if err != nil { - return false, result.Error(http.StatusInternalServerError, err.Error()) - } - if !sure { - return false, result.Unauthorized("不能访问不属于自己的园区。") - } - return true, nil -} - -func listAllParksUnderSessionUser(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - keyword := c.Query("keyword") - parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid, keyword) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已获取到指定用户下的园区。", fiber.Map{"parks": parks}) -} - -func listAllParksUnderSpecificUser(c *fiber.Ctx) error { - result := response.NewResult(c) - requestUserId := c.Params("uid") - keyword := c.Query("keyword") - parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId, keyword) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已获取到指定用户下的园区。", fiber.Map{"parks": parks}) -} - -type _ParkInfoFormData struct { - Name string `json:"name" form:"name"` - Region *string `json:"region" form:"region"` - Address *string `json:"address" form:"address"` - Contact *string `json:"contact" form:"contact"` - Phone *string `json:"phone" from:"phone"` - Area decimal.NullDecimal `json:"area" from:"area"` - Capacity decimal.NullDecimal `json:"capacity" from:"capacity"` - TenementQuantity decimal.NullDecimal `json:"tenement" from:"tenement"` - Category int8 `json:"category" form:"category"` - SubmeterType int8 `json:"submeter" form:"submeter"` -} - -func createNewPark(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - formData := new(_ParkInfoFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - newPark := new(model.Park) - copier.Copy(newPark, formData) - newPark.Id = uuid.New().String() - newPark.UserId = userSession.Uid - nameAbbr := tools.PinyinAbbr(newPark.Name) - newPark.Abbr = &nameAbbr - newPark.Enabled = true - err = service.ParkService.SaveNewPark(*newPark) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Created("新园区完成创建。") -} - -func modifyPark(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestParkId := c.Params("pid") - formData := new(_ParkInfoFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - park, err := service.ParkService.FetchParkDetail(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if userSession.Uid != park.UserId { - return result.Unauthorized("不能修改不属于自己的园区。") - } - copier.Copy(park, formData) - nameAbbr := tools.PinyinAbbr(formData.Name) - park.Abbr = &nameAbbr - err = service.ParkService.UpdateParkInfo(park) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定园区资料已更新。") -} - -func fetchParkDetail(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - park, err := service.ParkService.FetchParkDetail(requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已经获取到指定园区的信息。", fiber.Map{"park": park}) -} - -type _ParkStateFormData struct { - Enabled bool `json:"enabled" form:"enabled"` -} - -func changeParkEnableState(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - formData := new(_ParkStateFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - err = service.ParkService.ChangeParkState(userSession.Uid, requestParkId, formData.Enabled) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定园区的可用性状态已成功更新。") -} - -func deleteSpecificPark(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - requestParkId := c.Params("pid") - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - err = service.ParkService.DeletePark(userSession.Uid, requestParkId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Deleted("指定园区已成功删除。") -} diff --git a/controller/region.go b/controller/region.go deleted file mode 100644 index f509c54..0000000 --- a/controller/region.go +++ /dev/null @@ -1,40 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/response" - "electricity_bill_calc/service" - "net/http" - - "github.com/gofiber/fiber/v2" -) - -func InitializeRegionController(router *fiber.App) { - router.Get("/region/:rid", fetchRegions) - router.Get("/regions/:rid", fetchAllLeveledRegions) -} - -func fetchRegions(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParentId := c.Params("rid") - regions, err := service.RegionService.FetchSubRegions(requestParentId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if len(regions) == 0 { - return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)}) - } - return result.Json(http.StatusOK, "已经获取到相关的行政区划。", fiber.Map{"regions": regions}) -} - -func fetchAllLeveledRegions(c *fiber.Ctx) error { - result := response.NewResult(c) - requestRegionCode := c.Params("rid") - regions, err := service.RegionService.FetchAllParentRegions(requestRegionCode) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if len(regions) == 0 { - return result.Json(http.StatusNotFound, "未能获取到相关的行政区划。", fiber.Map{"regions": make([]string, 0)}) - } - return result.Json(http.StatusOK, "以及获取到相关的行政区划。", fiber.Map{"regions": regions}) -} diff --git a/controller/report.go b/controller/report.go deleted file mode 100644 index 6cf63c0..0000000 --- a/controller/report.go +++ /dev/null @@ -1,301 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/exceptions" - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "electricity_bill_calc/tools" - "net/http" - "strconv" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/jinzhu/copier" - "github.com/samber/lo" - "github.com/shopspring/decimal" -) - -func InitializeReportController(router *fiber.App) { - router.Get("/reports/with/drafts", security.EnterpriseAuthorize, fetchNewestReportOfParkWithDraft) - router.Post("/park/:pid/report", security.EnterpriseAuthorize, initializeNewReport) - router.Get("/report/:rid/step/state", security.EnterpriseAuthorize, fetchReportStepStates) - router.Get("/report/:rid/summary", security.EnterpriseAuthorize, fetchReportParkSummary) - router.Put("/report/:rid/summary", security.EnterpriseAuthorize, fillReportSummary) - router.Get("/report/:rid/summary/calculate", security.EnterpriseAuthorize, testCalculateReportSummary) - router.Post("/report/:rid/summary/calculate", security.EnterpriseAuthorize, progressReportSummary) - router.Put("/report/:rid/step/meter/register", security.EnterpriseAuthorize, progressEndUserRegister) - router.Post("/report/:rid/publish", security.EnterpriseAuthorize, publishReport) - router.Get("/reports", security.MustAuthenticated, searchReports) - router.Get("/report/:rid", security.MustAuthenticated, fetchReportPublicity) - router.Post("/report/:rid/calculate", security.EnterpriseAuthorize, calculateReport) -} - -func ensureReportBelongs(c *fiber.Ctx, result *response.Result, requestReportId string) (bool, error) { - _, err := _retreiveSession(c) - if err != nil { - return false, result.Unauthorized(err.Error()) - } - requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId) - if err != nil { - return false, result.NotFound(err.Error()) - } - if requestReport == nil { - return false, result.NotFound("指定报表未能找到。") - } - return ensureParkBelongs(c, result, requestReport.ParkId) -} - -func fetchNewestReportOfParkWithDraft(c *fiber.Ctx) error { - result := response.NewResult(c) - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - parks, err := service.ReportService.FetchParksWithNewestReport(userSession.Uid) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已获取到指定用户下所有园区的最新报表记录。", fiber.Map{"parks": parks}) -} - -func initializeNewReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestParkId := c.Params("pid") - userSession, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - if ensure, err := ensureParkBelongs(c, &result, requestParkId); !ensure { - return err - } - requestPeriod := c.Query("period") - reportPeriod, err := time.Parse("2006-01", requestPeriod) - if err != nil { - return result.NotAccept("提供的初始化期数格式不正确。") - } - valid, err := service.ReportService.IsNewPeriodValid(userSession.Uid, requestParkId, reportPeriod) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !valid { - return result.NotAccept("只能初始化已发布报表下一个月份的新报表。") - } - newId, err := service.ReportService.InitializeNewReport(requestParkId, reportPeriod) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Created("新一期报表初始化成功。", fiber.Map{"reportId": newId}) -} - -func fetchReportStepStates(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - requestReport, err := service.ReportService.RetreiveReportIndex(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json(http.StatusOK, "已经获取到指定报表的填写状态。", fiber.Map{"steps": requestReport.StepState}) -} - -func fetchReportParkSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - summary, err := service.ReportService.RetreiveReportSummary(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - if summary == nil { - return result.NotFound("指定报表未能找到。") - } - return result.Json(http.StatusOK, "已经获取到指定报表中的园区概况。", fiber.Map{"summary": summary}) -} - -type ReportSummaryFormData struct { - Overall decimal.Decimal `json:"overall" form:"overall"` - OverallFee decimal.Decimal `json:"overallFee" form:"overallFee"` - Critical decimal.Decimal `json:"critical" form:"critical"` - CriticalFee decimal.Decimal `json:"criticalFee" form:"criticalFee"` - Peak decimal.Decimal `json:"peak" form:"peak"` - PeakFee decimal.Decimal `json:"peakFee" form:"peakFee"` - Valley decimal.Decimal `json:"valley" form:"valley"` - ValleyFee decimal.Decimal `json:"valleyFee" form:"valleyFee"` - BasicFee decimal.Decimal `json:"basicFee" form:"basicFee"` - AdjustFee decimal.Decimal `json:"adjustFee" from:"adjustFee"` -} - -func fillReportSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - formData := new(ReportSummaryFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - originSummary, err := service.ReportService.RetreiveReportSummary(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - copier.Copy(originSummary, formData) - originSummary.ReportId = requestReportId - err = service.ReportService.UpdateReportSummary(originSummary) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Updated("指定电费公示报表中的园区概况基本数据已经完成更新。") -} - -func testCalculateReportSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - summary, err := service.ReportService.RetreiveReportSummary(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - summary.CalculatePrices() - calcResults := tools.ConvertStructToMap(summary) - return result.Json( - http.StatusOK, - "已完成园区概况的试计算。", - fiber.Map{ - "result": lo.PickByKeys( - calcResults, - []string{"overallPrice", "criticalPrice", "peakPrice", "flat", "flatFee", "flatPrice", "valleyPrice", "consumptionFee"}, - ), - }, - ) -} - -func progressReportSummary(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - err := service.ReportService.CalculateSummaryAndFinishStep(requestReportId) - if err != nil { - if nfErr, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound(nfErr.Error()) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - return result.Success("已经完成园区概况的计算,并可以进行到下一步骤。") -} - -func progressEndUserRegister(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - report, err := service.ReportService.RetreiveReportIndex(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - err = service.ReportService.ProgressReportRegisterEndUser(*report) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success("终端用户抄表编辑步骤已经完成。") -} - -func publishReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - report, err := service.ReportService.RetreiveReportIndex(requestReportId) - if err != nil { - return result.NotFound(err.Error()) - } - err = service.ReportService.PublishReport(*report) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success("指定的公示报表已经发布。") -} - -func searchReports(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 - } - } - requestPeriodString := c.Query("period") - var requestPeriod *time.Time = nil - if len(requestPeriodString) > 0 { - parsedPeriod, err := time.Parse("2006-01", requestPeriodString) - if err != nil { - return result.NotAccept("参数[period]的格式不正确。") - } - requestPeriod = lo.ToPtr(parsedPeriod) - } - requestKeyword := c.Query("keyword") - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - requestAllReports, err := strconv.ParseBool(c.Query("all", "false")) - if err != nil { - return result.NotAccept("查询参数[all]格式不正确。") - } - records, totalItems, err := service.ReportService.SearchReport(requestUser, requestPark, requestKeyword, requestPeriod, requestPage, !requestAllReports) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Success( - "已经取得符合条件的公示报表记录。", - response.NewPagedResponse(requestPage, totalItems).ToMap(), - fiber.Map{"reports": records}, - ) -} - -func fetchReportPublicity(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - publicity, err := service.ReportService.AssembleReportPublicity(requestReportId) - if err != nil { - if nfErr, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound(nfErr.Error()) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - return result.Success("已经取得指定公示报表的发布版本。", tools.ConvertStructToMap(publicity)) -} - -func calculateReport(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - err := service.CalculateService.ComprehensivelyCalculateReport(requestReportId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success("指定公示报表中的数据已经计算完毕。") -} diff --git a/controller/statistics.go b/controller/statistics.go deleted file mode 100644 index 413efeb..0000000 --- a/controller/statistics.go +++ /dev/null @@ -1,73 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "net/http" - - "github.com/gofiber/fiber/v2" -) - -func InitializeStatisticsController(router *fiber.App) { - router.Get("/audits", security.OPSAuthorize, currentAuditAmount) - router.Get("/stat/reports", security.MustAuthenticated, statReports) -} - -func currentAuditAmount(c *fiber.Ctx) error { - result := response.NewResult(c) - amount, err := service.WithdrawService.AuditWaits() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已经获取到指定的统计信息。", fiber.Map{ - "amounts": map[string]int64{ - "withdraw": amount, - }, - }) -} - -func statReports(c *fiber.Ctx) error { - result := response.NewResult(c) - session, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - var ( - enterprises int64 = 0 - parks int64 = 0 - reports []model.ParkPeriodStatistics - ) - if session.Type != 0 { - enterprises, err = service.StatisticsService.EnabledEnterprises() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - parks, err = service.StatisticsService.EnabledParks() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - reports, err = service.StatisticsService.ParksNewestState() - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } else { - parks, err = service.StatisticsService.EnabledParks(session.Uid) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - reports, err = service.StatisticsService.ParksNewestState(session.Uid) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - - return result.Json(http.StatusOK, "已经完成园区报告的统计。", fiber.Map{ - "statistics": fiber.Map{ - "enterprises": enterprises, - "parks": parks, - "reports": reports, - }, - }) -} diff --git a/controller/user.go b/controller/user.go deleted file mode 100644 index 0689cfc..0000000 --- a/controller/user.go +++ /dev/null @@ -1,357 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/cache" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/model" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "electricity_bill_calc/tools" - "fmt" - "net/http" - "strconv" - - "github.com/gofiber/fiber/v2" - "github.com/shopspring/decimal" -) - -func InitializeUserController(router *fiber.App) { - router.Delete("/password/:uid", security.OPSAuthorize, invalidUserPassword) - router.Delete("/login", security.MustAuthenticated, logout) - router.Put("/password", resetUserPassword) - router.Get("/accounts", security.ManagementAuthorize, listPagedUser) - router.Post("/login", login) - router.Put("/account/enabled/state", security.OPSAuthorize, switchUserEnabling) - router.Post("/account", security.OPSAuthorize, createOPSAndManagementAccount) - router.Get("/account/:uid", security.MustAuthenticated, getUserDetail) - router.Post("/enterprise", security.OPSAuthorize, createEnterpriseAccount) - router.Put("/account/:uid", security.OPSAuthorize, modifyAccountDetail) - router.Get("/enterprise/quick/search", security.OPSAuthorize, quickSearchEnterprise) - router.Get("/expiration", security.EnterpriseAuthorize, fetchExpiration) -} - -type _LoginFormData struct { - Username string `json:"uname"` - Password string `json:"upass"` - Type int8 `json:"type"` -} - -func login(c *fiber.Ctx) error { - result := response.NewResult(c) - loginData := new(_LoginFormData) - if err := c.BodyParser(loginData); err != nil { - return result.Error(http.StatusInternalServerError, "表单解析失败。") - } - var ( - session *model.Session - err error - ) - if loginData.Type == model.USER_TYPE_ENT { - session, err = service.UserService.ProcessEnterpriseUserLogin(loginData.Username, loginData.Password) - } else { - session, err = service.UserService.ProcessManagementUserLogin(loginData.Username, loginData.Password) - } - if err != nil { - if authError, ok := err.(*exceptions.AuthenticationError); ok { - if authError.NeedReset { - return result.LoginNeedReset() - } - return result.Error(int(authError.Code), authError.Message) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - return result.LoginSuccess(session) -} - -func logout(c *fiber.Ctx) error { - result := response.NewResult(c) - session := c.Locals("session") - if session == nil { - return result.Success("用户会话已结束。") - } - _, err := cache.ClearSession(session.(*model.Session).Token) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Success("用户已成功登出系统。") -} - -func invalidUserPassword(c *fiber.Ctx) error { - result := response.NewResult(c) - targetUserId := c.Params("uid") - verifyCode, err := service.UserService.InvalidUserPassword(targetUserId) - if _, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound("未找到指定用户。") - } - if _, ok := err.(exceptions.UnsuccessfulOperationError); ok { - return result.NotAccept("未能成功更新用户的密码。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusAccepted, "用户密码已经失效", fiber.Map{"verify": verifyCode}) -} - -type _ResetPasswordFormData struct { - VerifyCode string `json:"verifyCode"` - Username string `json:"uname"` - NewPassword string `json:"newPass"` -} - -func resetUserPassword(c *fiber.Ctx) error { - result := response.NewResult(c) - resetForm := new(_ResetPasswordFormData) - if err := c.BodyParser(resetForm); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - verified, err := service.UserService.VerifyUserPassword(resetForm.Username, resetForm.VerifyCode) - if _, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound("指定的用户不存在。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if !verified { - return result.Error(http.StatusUnauthorized, "验证码不正确。") - } - completed, err := service.UserService.ResetUserPassword(resetForm.Username, resetForm.NewPassword) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - if completed { - return result.Updated("用户凭据已更新。") - } - return result.NotAccept("用户凭据未能成功更新。") -} - -func listPagedUser(c *fiber.Ctx) error { - result := response.NewResult(c) - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - requestKeyword := c.Query("keyword") - requestUserType, err := strconv.Atoi(c.Query("type", "-1")) - if err != nil { - return result.NotAccept("查询参数[type]格式不正确。") - } - var requestUserStat *bool - state, err := strconv.ParseBool(c.Query("state")) - if err != nil { - requestUserStat = nil - } else { - requestUserStat = &state - } - users, total, err := service.UserService.ListUserDetail(requestKeyword, requestUserType, requestUserStat, requestPage) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, - "已取得符合条件的用户集合。", - response.NewPagedResponse(requestPage, total).ToMap(), - fiber.Map{"accounts": users}, - ) -} - -type _UserStateChangeFormData struct { - UserID string `json:"uid" form:"uid"` - Enabled bool `json:"enabled" form:"enabled"` -} - -func switchUserEnabling(c *fiber.Ctx) error { - result := response.NewResult(c) - switchForm := new(_UserStateChangeFormData) - if err := c.BodyParser(switchForm); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - err := service.UserService.SwitchUserState(switchForm.UserID, switchForm.Enabled) - if err != nil { - if nfErr, ok := err.(*exceptions.NotFoundError); ok { - return result.NotFound(nfErr.Message) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - return result.Updated("用户状态已经更新。") -} - -type _OPSAccountCreationFormData struct { - Username string `json:"username" form:"username"` - Name string `json:"name" form:"name"` - Contact *string `json:"contact" form:"contact"` - Phone *string `json:"phone" form:"phone"` - Type int `json:"type" form:"type"` -} - -func createOPSAndManagementAccount(c *fiber.Ctx) error { - result := response.NewResult(c) - creationForm := new(_OPSAccountCreationFormData) - if err := c.BodyParser(creationForm); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - exists, err := service.UserService.IsUsernameExists(creationForm.Username) - if exists { - return result.Conflict("指定的用户名已经被使用了。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - newUser := new(model.User) - newUser.Username = creationForm.Username - newUser.Type = int8(creationForm.Type) - newUser.Enabled = true - newUserDetail := new(model.UserDetail) - newUserDetail.Name = &creationForm.Name - newUserDetail.Contact = creationForm.Contact - newUserDetail.Phone = creationForm.Phone - newUserDetail.UnitServiceFee = decimal.Zero - newUserDetail.ServiceExpiration, _ = model.ParseDate("2099-12-31") - verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - cache.AbolishRelation("user") - return result.Json(http.StatusCreated, "用户已经成功创建。", fiber.Map{"verify": verifyCode}) -} - -func getUserDetail(c *fiber.Ctx) error { - result := response.NewResult(c) - targetUserId := c.Params("uid") - exists, err := service.UserService.IsUserExists(targetUserId) - if !exists { - return result.NotFound("指定的用户不存在。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - userDetail, err := service.UserService.FetchUserDetail(targetUserId) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "用户详细信息已获取到。", fiber.Map{"user": userDetail}) -} - -type _EnterpriseCreationFormData struct { - Username string `json:"username" form:"username"` - Name string `json:"name" form:"name"` - Region *string `json:"region" form:"region"` - Address *string `json:"address" form:"address"` - Contact *string `json:"contact" form:"contact"` - Phone *string `json:"phone" form:"phone"` - UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"` -} - -func createEnterpriseAccount(c *fiber.Ctx) error { - result := response.NewResult(c) - creationForm := new(_EnterpriseCreationFormData) - if err := c.BodyParser(creationForm); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - exists, err := service.UserService.IsUsernameExists(creationForm.Username) - if exists { - return result.Conflict("指定的用户名已经被使用了。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - newUser := new(model.User) - newUser.Username = creationForm.Username - newUser.Type = model.USER_TYPE_ENT - newUser.Enabled = true - newUserDetail := new(model.UserDetail) - newUserDetail.Name = &creationForm.Name - newUserDetail.Contact = creationForm.Contact - newUserDetail.Phone = creationForm.Phone - newUserDetail.UnitServiceFee, err = decimal.NewFromString(*creationForm.UnitServiceFee) - if err != nil { - return result.BadRequest("用户月服务费无法解析。") - } - newUserDetail.ServiceExpiration = model.NewEmptyDate() - - verifyCode, err := service.UserService.CreateUser(newUser, newUserDetail) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - cache.AbolishRelation("user") - return result.Json(http.StatusCreated, "用户已经成功创建。", fiber.Map{"verify": verifyCode}) -} - -type _AccountModificationFormData struct { - Name string `json:"name" form:"name"` - Region *string `json:"region" form:"region"` - Address *string `json:"address" form:"address"` - Contact *string `json:"contact" form:"contact"` - Phone *string `json:"phone" form:"phone"` - UnitServiceFee *string `json:"unitServiceFee" form:"unitServiceFee"` -} - -func modifyAccountDetail(c *fiber.Ctx) error { - result := response.NewResult(c) - targetUserId := c.Params("uid") - modForm := new(_AccountModificationFormData) - if err := c.BodyParser(modForm); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - exists, err := service.UserService.IsUserExists(targetUserId) - if !exists { - return result.NotFound("指定的用户不存在。") - } - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - newUserInfo := new(model.UserDetail) - newUserInfo.Id = targetUserId - newUserInfo.Name = &modForm.Name - if len(modForm.Name) > 0 { - abbr := tools.PinyinAbbr(modForm.Name) - newUserInfo.Abbr = &abbr - } - newUserInfo.Region = modForm.Region - newUserInfo.Address = modForm.Address - newUserInfo.Contact = modForm.Contact - newUserInfo.Phone = modForm.Phone - newUserInfo.UnitServiceFee, err = decimal.NewFromString(*modForm.UnitServiceFee) - if err != nil { - return result.BadRequest("用户月服务费无法解析。") - } - _, err = global.DB.NewUpdate().Model(newUserInfo). - WherePK(). - Column("name", "abbr", "region", "address", "contact", "phone", "unit_service_fee"). - Exec(c.Context()) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - cache.AbolishRelation(fmt.Sprintf("user:%s", targetUserId)) - return result.Updated("指定用户的信息已经更新。") -} - -func quickSearchEnterprise(c *fiber.Ctx) error { - result := response.NewResult(c) - keyword := c.Query("keyword") - searchResult, err := service.UserService.SearchLimitUsers(keyword, 6) - if err != nil { - return result.Error(http.StatusInternalServerError, err.Error()) - } - return result.Json(http.StatusOK, "已查询到存在符合条件的企业", fiber.Map{"users": searchResult}) -} - -func fetchExpiration(c *fiber.Ctx) error { - result := response.NewResult(c) - session, err := _retreiveSession(c) - if err != nil { - return result.Unauthorized(err.Error()) - } - user, err := service.UserService.FetchUserDetail(session.Uid) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, - "已经取得用户的服务期限信息", - fiber.Map{"expiration": user.ServiceExpiration.Format("2006-01-02")}, - ) -} diff --git a/controller/withdraw.go b/controller/withdraw.go deleted file mode 100644 index 4021a93..0000000 --- a/controller/withdraw.go +++ /dev/null @@ -1,81 +0,0 @@ -package controller - -import ( - "electricity_bill_calc/exceptions" - "electricity_bill_calc/response" - "electricity_bill_calc/security" - "electricity_bill_calc/service" - "net/http" - "strconv" - - "github.com/gofiber/fiber/v2" -) - -func InitializeWithdrawController(router *fiber.App) { - router.Delete("/publicity/:pid", security.EnterpriseAuthorize, applyReportWithdraw) - router.Get("/withdraws", security.OPSAuthorize, fetchWithdrawsWaitingAutdit) - router.Put("/withdraw/:rid", security.OPSAuthorize, auditWithdraw) -} - -func applyReportWithdraw(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("pid") - if ensure, err := ensureReportBelongs(c, &result, requestReportId); !ensure { - return err - } - deleted, err := service.WithdrawService.ApplyWithdraw(requestReportId) - if err != nil { - if nfErr, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound(nfErr.Error()) - } else if ioErr, ok := err.(exceptions.ImproperOperateError); ok { - return result.NotAccept(ioErr.Error()) - } else { - return result.Error(http.StatusInternalServerError, err.Error()) - } - } - if !deleted { - return result.Error(http.StatusInternalServerError, "未能完成公示报表的申请撤回操作。") - } - return result.Success("指定的公示报表已经申请撤回。") -} - -func fetchWithdrawsWaitingAutdit(c *fiber.Ctx) error { - result := response.NewResult(c) - keyword := c.Query("keyword") - requestPage, err := strconv.Atoi(c.Query("page", "1")) - if err != nil { - return result.NotAccept("查询参数[page]格式不正确。") - } - reports, totalitems, err := service.WithdrawService.FetchPagedWithdrawApplies(requestPage, keyword) - if err != nil { - return result.NotFound(err.Error()) - } - return result.Json( - http.StatusOK, - "已经取得符合条件的等待审核的撤回申请。", - response.NewPagedResponse(requestPage, totalitems).ToMap(), - fiber.Map{"records": reports}, - ) -} - -type WithdrawAuditFormData struct { - Audit bool `json:"audit" form:"audit"` -} - -func auditWithdraw(c *fiber.Ctx) error { - result := response.NewResult(c) - requestReportId := c.Params("rid") - formData := new(WithdrawAuditFormData) - if err := c.BodyParser(formData); err != nil { - return result.UnableToParse("无法解析提交的数据。") - } - err := service.WithdrawService.AuditWithdraw(requestReportId, formData.Audit) - if err != nil { - if nfErr, ok := err.(exceptions.NotFoundError); ok { - return result.NotFound(nfErr.Error()) - } else { - return result.NotAccept(err.Error()) - } - } - return result.Success("指定公示报表的撤回申请已经完成审核") -} diff --git a/excel/abstract.go b/excel/abstract.go index 7d5c27a..2d98237 100644 --- a/excel/abstract.go +++ b/excel/abstract.go @@ -1,7 +1,6 @@ package excel import ( - "electricity_bill_calc/model" "electricity_bill_calc/tools" "encoding/json" "errors" @@ -19,7 +18,6 @@ import ( type ExcelTemplateGenerator interface { Close() WriteTo(w io.Writer) (int64, error) - WriteMeterData(meters []model.EndUserDetail) error } type ColumnRecognizer struct { diff --git a/excel/end_user.go b/excel/end_user.go deleted file mode 100644 index c051a33..0000000 --- a/excel/end_user.go +++ /dev/null @@ -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) -} diff --git a/excel/meter_archive.go b/excel/meter_archive.go deleted file mode 100644 index a8d8cd6..0000000 --- a/excel/meter_archive.go +++ /dev/null @@ -1,21 +0,0 @@ -package excel - -import ( - "electricity_bill_calc/model" - "io" -) - -var meter04kVExcelRecognizers = []*ColumnRecognizer{ - {Pattern: []string{"表号"}, Tag: "code", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"户名"}, Tag: "name", MatchIndex: -1}, - {Pattern: []string{"户址"}, Tag: "address", MatchIndex: -1}, - {Pattern: []string{"联系人"}, Tag: "contact", MatchIndex: -1}, - {Pattern: []string{"电话"}, Tag: "phone", MatchIndex: -1}, - {Pattern: []string{"倍率"}, Tag: "ratio", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"序号"}, Tag: "seq", MatchIndex: -1, MustFill: true}, - {Pattern: []string{"公用设备"}, Tag: "public", MatchIndex: -1, MustFill: true}, -} - -func NewMeterArchiveExcelAnalyzer(file io.Reader) (*ExcelAnalyzer[model.Meter04KV], error) { - return NewExcelAnalyzer[model.Meter04KV](file, meter04kVExcelRecognizers) -} diff --git a/excel/meter_non_pv_template.go b/excel/meter_non_pv_template.go deleted file mode 100644 index 33392fd..0000000 --- a/excel/meter_non_pv_template.go +++ /dev/null @@ -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 -} diff --git a/excel/meter_pv_template.go b/excel/meter_pv_template.go deleted file mode 100644 index 0121699..0000000 --- a/excel/meter_pv_template.go +++ /dev/null @@ -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 -} diff --git a/global/db.go b/global/db.go index 17d01a3..6ed62cd 100644 --- a/global/db.go +++ b/global/db.go @@ -1,60 +1,92 @@ package global import ( - "database/sql" + "context" "fmt" "time" "electricity_bill_calc/config" "electricity_bill_calc/logger" - "github.com/uptrace/bun" - "github.com/uptrace/bun/dialect/pgdialect" - "github.com/uptrace/bun/driver/pgdriver" - "go.uber.org/zap/zapcore" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/samber/lo" + "go.uber.org/zap" ) var ( - DB *bun.DB + DB *pgxpool.Pool ) func SetupDatabaseConnection() error { - // connStr := fmt.Sprintf( - // "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai connect_timeout=0 tcp_user_timeout=180000", - // config.DatabaseSettings.Host, - // config.DatabaseSettings.User, - // config.DatabaseSettings.Pass, - // config.DatabaseSettings.DB, - // config.DatabaseSettings.Port, - // ) - pgconn := pgdriver.NewConnector( - pgdriver.WithNetwork("tcp"), - pgdriver.WithAddr(fmt.Sprintf("%s:%d", config.DatabaseSettings.Host, - config.DatabaseSettings.Port)), - pgdriver.WithUser(config.DatabaseSettings.User), - pgdriver.WithInsecure(true), - pgdriver.WithPassword(config.DatabaseSettings.Pass), - pgdriver.WithDatabase(config.DatabaseSettings.DB), - pgdriver.WithDialTimeout(30*time.Second), - pgdriver.WithReadTimeout(3*time.Minute), - pgdriver.WithWriteTimeout(10*time.Minute), - ) - sqldb := sql.OpenDB(pgconn) - DB = bun.NewDB(sqldb, pgdialect.New()) - DB.AddQueryHook(logger.NewQueryHook(logger.QueryHookOptions{ - LogSlow: 3 * time.Second, - Logger: logger.Named("PG"), - QueryLevel: zapcore.DebugLevel, - ErrorLevel: zapcore.ErrorLevel, - SlowLevel: zapcore.WarnLevel, - ErrorTemplate: "{{.Operation}}[{{.Duration}}]: {{.Query}}: {{.Error}}", - MessageTemplate: "{{.Operation}}[{{.Duration}}]: {{.Query}}", - })) - DB.SetMaxIdleConns(config.DatabaseSettings.MaxIdleConns) - DB.SetMaxOpenConns(config.DatabaseSettings.MaxOpenConns) - DB.SetConnMaxIdleTime(10 * time.Minute) - DB.SetConnMaxLifetime(60 * time.Minute) - DB.Ping() + connConfig := &pgx.ConnConfig{ + Config: pgconn.Config{ + Host: config.DatabaseSettings.Host, + Port: uint16(config.DatabaseSettings.Port), + User: config.DatabaseSettings.User, + Password: config.DatabaseSettings.Pass, + Database: config.DatabaseSettings.DB, + TLSConfig: nil, + ConnectTimeout: 0 * time.Second, + RuntimeParams: map[string]string{"application_name": "elec_service_go"}, + }, + Tracer: QueryLogger{ + logger: logger.Named("PG"), + }, + } + poolConfig := &pgxpool.Config{ + ConnConfig: connConfig, + MaxConnLifetime: 60 * time.Minute, + MaxConnIdleTime: 10 * time.Minute, + HealthCheckPeriod: 10 * time.Second, + MaxConns: int32(config.DatabaseSettings.MaxOpenConns), + MinConns: int32(config.DatabaseSettings.MaxIdleConns), + } + DB, _ = pgxpool.NewWithConfig(context.Background(), poolConfig) return nil } + +type QueryLogger struct { + logger *zap.Logger +} + +func (ql QueryLogger) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context { + ql.logger.Info(fmt.Sprintf("将要执行查询: %s", data.SQL)) + ql.logger.Info("查询参数", lo.Map(data.Args, func(elem any, index int) zap.Field { + return zap.Any(fmt.Sprintf("[Arg %d]: ", index), elem) + })...) + return ctx +} + +func (ql QueryLogger) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) { + var logFunc func(string, ...zap.Field) + var templateString string + if data.Err != nil { + logFunc = ql.logger.Error + templateString = "命令 [%s] 执行失败。" + } else { + logFunc = ql.logger.Info + templateString = "命令 [%s] 执行成功。" + } + switch { + case data.CommandTag.Update(): + fallthrough + case data.CommandTag.Delete(): + fallthrough + case data.CommandTag.Insert(): + logFunc( + fmt.Sprintf(templateString, data.CommandTag.String()), + zap.Error(data.Err), + zap.Any("affected", data.CommandTag.RowsAffected())) + case data.CommandTag.Select(): + logFunc( + fmt.Sprintf(templateString, data.CommandTag.String()), + zap.Error(data.Err)) + default: + logFunc( + fmt.Sprintf(templateString, data.CommandTag.String()), + zap.Error(data.Err)) + } +} diff --git a/go.mod b/go.mod index 9abd945..09bfea7 100644 --- a/go.mod +++ b/go.mod @@ -22,14 +22,21 @@ require ( require ( github.com/andybalholm/brotli v1.0.4 // indirect + github.com/georgysavva/scany/v2 v2.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.3.1 // 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.15.0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/sync v0.1.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect mellium.im/sasl v0.3.0 // indirect ) @@ -59,11 +66,11 @@ require ( github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect + golang.org/x/crypto v0.6.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect - golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 846c8ed..f0bfce6 100644 --- a/go.sum +++ b/go.sum @@ -66,9 +66,12 @@ github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwV github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fufuok/utils v0.7.13 h1:FGx8Mnfg0ZB8HdVz1X60JJ2kFu1rtcsFDYUxUTzNKkU= github.com/fufuok/utils v0.7.13/go.mod h1:ztIaorPqZGdbvmW3YlwQp80K8rKJmEy6xa1KwpJSsmk= +github.com/georgysavva/scany/v2 v2.0.0 h1:RGXqxDv4row7/FYoK8MRXAZXqoWF/NM+NP0q50k3DKU= +github.com/georgysavva/scany/v2 v2.0.0/go.mod h1:sigOdh+0qb/+aOs3TVhehVT10p8qJL7K/Zhyz8vWo38= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -135,10 +138,20 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= +github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= +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= @@ -156,8 +169,10 @@ 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/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-sqlite3 v1.14.6/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= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -190,6 +205,8 @@ github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK 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/samber/lo v1.27.0 h1:GOyDWxsblvqYobqsmUuMddPa2/mMzkKyojlXol4+LaQ= @@ -216,6 +233,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= @@ -272,6 +290,10 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0 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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -344,6 +366,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -363,6 +387,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -404,6 +430,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -415,6 +443,8 @@ 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.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/logger/bunhook.go b/logger/bunhook.go deleted file mode 100644 index e29ce1a..0000000 --- a/logger/bunhook.go +++ /dev/null @@ -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) -} diff --git a/main.go b/main.go index cb7965f..37628ab 100644 --- a/main.go +++ b/main.go @@ -1,25 +1,17 @@ package main import ( - "database/sql" "electricity_bill_calc/cache" "electricity_bill_calc/config" "electricity_bill_calc/global" "electricity_bill_calc/logger" - "electricity_bill_calc/migration" "electricity_bill_calc/model" "electricity_bill_calc/router" "electricity_bill_calc/service" - "encoding/csv" "fmt" - "io" - "os" - "strconv" "time" - "github.com/samber/lo" "github.com/shopspring/decimal" - "github.com/uptrace/bun/migrate" "go.uber.org/zap" ) @@ -27,98 +19,27 @@ func init() { l := logger.Named("Init") err := config.SetupSetting() if err != nil { - l.Fatal("Configuration load failed.", zap.Error(err)) + l.Fatal("服务配置文件加载失败!", zap.Error(err)) } - l.Info("Configuration loaded!") + l.Info("服务配置已经完成加载。") err = global.SetupDatabaseConnection() if err != nil { - l.Fatal("Main Database connect failed.", zap.Error(err)) - } - l.Info("Main Database connected!") - - migrator := migrate.NewMigrator(global.DB, migration.Migrations) - ctx, cancel := global.TimeoutContext(12) - defer cancel() - err = migrator.Init(ctx) - if err != nil { - l.Fatal("Database migration unable to initialized.", zap.Error(err)) - } - group, err := migrator.Migrate(ctx) - if err != nil { - l.Fatal("Database migrate failed.", zap.Error(err)) - } - if group.IsZero() { - l.Info("There are no new migrations to run (database is up to date)") + l.Fatal("主数据库连接失败!", zap.Error(err)) } + l.Info("主数据库已经连接。") err = global.SetupRedisConnection() if err != nil { - l.Fatal("Main Cache Database connect failed.", zap.Error(err)) + l.Fatal("主缓存数据库连接失败!", zap.Error(err)) } - l.Info("Main Cache Database connected!") - - err = initializeRegions() - if err != nil { - l.Fatal("Regions initialize failed.", zap.Error(err)) - } - l.Info("Regions synchronized.") + l.Info("主缓存数据库已经连接。") err = intializeSingularity() if err != nil { - l.Fatal("Singularity account intialize failed.", zap.Error(err)) + l.Fatal("奇点账号初始化失败。", zap.Error(err)) } - l.Info("Singularity account intialized.") -} - -func initializeRegions() error { - ctx, cancel := global.TimeoutContext() - defer cancel() - logger.Info("Synchronize regions...") - regionCsvFile, err := os.Open("regions.csv") - if err != nil { - return fmt.Errorf("region initialize file is not found: %w", err) - } - defer regionCsvFile.Close() - - var existRegions = make([]string, 0) - err = global.DB.NewSelect().Model((*model.Region)(nil)). - Column("code"). - Scan(ctx, &existRegions) - if err != nil { - return fmt.Errorf("unable to retreive regions from database: %w", err) - } - - regionCsv := csv.NewReader(regionCsvFile) - transaction, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return fmt.Errorf("unable to intiate database transaction: %w", err) - } - for { - record, err := regionCsv.Read() - if err == io.EOF { - break - } - if lo.Contains(existRegions, record[0]) { - continue - } - level, err := strconv.Atoi(record[2]) - if err != nil { - continue - } - if _, err := transaction.NewInsert().Model(&model.Region{ - Code: record[0], - Name: record[1], - Level: level, - Parent: record[3], - }).Exec(ctx); err != nil { - return fmt.Errorf("region synchronize in failed: %v, %w", record, err) - } - } - if err = transaction.Commit(); err != nil { - return fmt.Errorf("synchronize regions to database failed: %w", err) - } - return nil + l.Info("奇点账号已经完成初始化。") } func intializeSingularity() error { @@ -159,7 +80,9 @@ func intializeSingularity() error { func DBConnectionKeepLive() { for range time.Tick(30 * time.Second) { - err := global.DB.Ping() + ctx, cancel := global.TimeoutContext() + defer cancel() + err := global.DB.Ping(ctx) if err != nil { continue } diff --git a/model/end_user_detail.go b/model/end_user_detail.go deleted file mode 100644 index 4c93e52..0000000 --- a/model/end_user_detail.go +++ /dev/null @@ -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"` -} diff --git a/model/maintenance_fee.go b/model/maintenance_fee.go deleted file mode 100644 index 47dcb8a..0000000 --- a/model/maintenance_fee.go +++ /dev/null @@ -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"` -} diff --git a/model/meter_04kv.go b/model/meter_04kv.go deleted file mode 100644 index 407f79b..0000000 --- a/model/meter_04kv.go +++ /dev/null @@ -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 -} diff --git a/model/park.go b/model/park.go deleted file mode 100644 index 326ee6d..0000000 --- a/model/park.go +++ /dev/null @@ -1,89 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/jinzhu/copier" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" -) - -const ( - CATEGORY_TWO_PART int8 = iota - CATEGORY_SINGLE_PV - CATEGORY_SINGLE_NON_PV -) - -const ( - CUSTOMER_METER_NON_PV int8 = iota - CUSTOMER_METER_PV -) - -type Park struct { - bun.BaseModel `bun:"table:park,alias:p"` - CreatedAndModified `bun:"extend"` - Deleted `bun:"extend"` - Id string `bun:",pk,notnull" json:"id"` - UserId string `bun:",notnull" json:"userId"` - Name string `bun:",notnull" json:"name"` - Abbr *string `json:"abbr"` - Area decimal.NullDecimal `bun:"type:numeric" json:"area"` - TenementQuantity decimal.NullDecimal `bun:"type:numeric" json:"tenement"` - Capacity decimal.NullDecimal `bun:"type:numeric" json:"capacity"` - Category int8 `bun:"type:smallint,notnull" json:"category"` - SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"` - Region *string `json:"region"` - Address *string `json:"address"` - Contact *string `json:"contact"` - Phone *string `json:"phone"` - Enabled bool `bun:",notnull" json:"enabled"` - EnterpriseIndex *User `bun:"rel:belongs-to,join:user_id=id" json:"-"` - Enterprise *UserDetail `bun:"rel:belongs-to,join:user_id=id" json:"-"` - MaintenanceFees []*MaintenanceFee `bun:"rel:has-many,join:id=park_id" json:"-"` - Meters []*Meter04KV `bun:"rel:has-many,join:id=park_id" json:"-"` - Reports []*Report `bun:"rel:has-many,join:id=park_id" json:"-"` -} - -type ParkSimplified struct { - bun.BaseModel `bun:"table:park,alias:p"` - Id string `bun:",pk,notnull" json:"id"` - UserId string `bun:",notnull" json:"userId"` - Name string `bun:",notnull" json:"name"` - Abbr *string `json:"abbr"` - Area decimal.NullDecimal `json:"area"` - TenementQuantity decimal.NullDecimal `json:"tenement"` - Capacity decimal.NullDecimal `json:"capacity"` - Category int8 `bun:"type:smallint,notnull" json:"category"` - SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"` - Region *string `json:"region"` - Address *string `json:"address"` - Contact *string `json:"contact"` - Phone *string `json:"phone"` -} - -type ParkPeriodStatistics struct { - Id string `bun:"park__id,notnull" json:"id"` - Name string `bun:"park__name,notnull" json:"name"` - Period *Date `bun:"type:date" json:"period"` -} - -func FromPark(park Park) ParkSimplified { - dest := ParkSimplified{} - copier.Copy(&dest, park) - return dest -} - -var _ bun.BeforeAppendModelHook = (*Park)(nil) - -func (p *Park) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - p.CreatedAt = oprTime - p.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - p.LastModifiedAt = &oprTime - } - return nil -} diff --git a/model/publicity.go b/model/publicity.go deleted file mode 100644 index 088e3cd..0000000 --- a/model/publicity.go +++ /dev/null @@ -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"` -} diff --git a/model/region.go b/model/region.go deleted file mode 100644 index 8fecddf..0000000 --- a/model/region.go +++ /dev/null @@ -1,11 +0,0 @@ -package model - -import "github.com/uptrace/bun" - -type Region struct { - bun.BaseModel `bun:"table:region,alias:r"` - Code string `bun:",pk,notnull" json:"code"` - Name string `bun:",notnull" json:"name"` - Level int `bun:",notnull" json:"level"` - Parent string `bun:",notnull" json:"parent"` -} diff --git a/model/report.go b/model/report.go deleted file mode 100644 index 0192b6d..0000000 --- a/model/report.go +++ /dev/null @@ -1,99 +0,0 @@ -package model - -import ( - "context" - "time" - - "github.com/uptrace/bun" -) - -const ( - REPORT_NOT_WITHDRAW int8 = iota - REPORT_WITHDRAW_APPLIED - REPORT_WITHDRAW_DENIED - REPORT_WITHDRAW_GRANTED -) - -type Report struct { - bun.BaseModel `bun:"table:report,alias:r"` - CreatedAndModified `bun:"extend"` - Id string `bun:",pk,notnull" json:"id"` - ParkId string `bun:",notnull" json:"parkId"` - Period time.Time `bun:"type:date,notnull" json:"period" time_format:"simple_date" time_location:"shanghai"` - Category int8 `bun:"type:smallint,notnull" json:"category"` - SubmeterType int8 `bun:"meter_04kv_type,type:smallint,notnull" json:"meter04kvType"` - StepState Steps `bun:"type:jsonb,notnull" json:"stepState"` - Published bool `bun:",notnull" json:"published"` - PublishedAt *time.Time `bun:"type:timestamptz,nullzero" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"` - Withdraw int8 `bun:"type:smallint,notnull" json:"withdraw"` - LastWithdrawAppliedAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"` - LastWithdrawAuditAt *time.Time `bun:"type:timestamptz,nullzero" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"` - Park *Park `bun:"rel:belongs-to,join:park_id=id" json:"-"` - Summary *ReportSummary `bun:"rel:has-one,join:id=report_id" json:"-"` - WillDilutedFees []*WillDilutedFee `bun:"rel:has-many,join:id=report_id" json:"-"` - EndUsers []*EndUserDetail `bun:"rel:has-many,join:id=report_id,join:park_id=park_id" json:"-"` -} - -type Steps struct { - Summary bool `json:"summary"` - WillDiluted bool `json:"willDiluted"` - Submeter bool `json:"submeter"` - Calculate bool `json:"calculate"` - Preview bool `json:"preview"` - Publish bool `json:"publish"` -} - -func NewSteps() Steps { - return Steps{ - Summary: false, - WillDiluted: false, - Submeter: false, - Calculate: false, - Preview: false, - Publish: false, - } -} - -type ParkNewestReport struct { - Park Park `bun:"extends" json:"park"` - Report *Report `bun:"extends" json:"report"` -} - -func (p *ParkNewestReport) AfterLoad() { - if p.Report != nil && len(p.Report.Id) == 0 { - p.Report = nil - } -} - -type ReportIndexSimplified struct { - bun.BaseModel `bun:"table:report,alias:r"` - Id string `bun:",pk,notnull" json:"id"` - ParkId string `bun:",notnull" json:"parkId"` - Period Date `bun:"type:date,notnull" json:"period"` - StepState Steps `bun:"type:jsonb,notnull" json:"stepState"` - Published bool `bun:",notnull" json:"published"` - PublishedAt *time.Time `bun:"type:timestampz" json:"publishedAt" time_format:"simple_datetime" time_location:"shanghai"` - Withdraw int8 `bun:"type:smallint,notnull" json:"withdraw"` - LastWithdrawAppliedAt *time.Time `bun:"type:timestamptz" json:"lastWithdrawAppliedAt" time_format:"simple_datetime" time_location:"shanghai"` - LastWithdrawAuditAt *time.Time `bun:"type:timestamptz" json:"lastWithdrawAuditAt" time_format:"simple_datetime" time_location:"shanghai"` -} - -type JoinedReportForWithdraw struct { - Report Report `bun:"extends" json:"report"` - Park ParkSimplified `bun:"extends" json:"park"` - User UserDetailSimplified `bun:"extends" json:"user"` -} - -var _ bun.BeforeAppendModelHook = (*Report)(nil) - -func (p *Report) BeforeAppendModel(ctx context.Context, query bun.Query) error { - oprTime := time.Now() - switch query.(type) { - case *bun.InsertQuery: - p.CreatedAt = oprTime - p.LastModifiedAt = &oprTime - case *bun.UpdateQuery: - p.LastModifiedAt = &oprTime - } - return nil -} diff --git a/model/report_summary.go b/model/report_summary.go deleted file mode 100644 index 467c955..0000000 --- a/model/report_summary.go +++ /dev/null @@ -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) - } -} diff --git a/model/shared.go b/model/shared.go deleted file mode 100644 index 84d2fe0..0000000 --- a/model/shared.go +++ /dev/null @@ -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"` -} diff --git a/model/user.go b/model/user.go index 3420fb7..391df8a 100644 --- a/model/user.go +++ b/model/user.go @@ -1,48 +1,7 @@ package model -import ( - "context" - "time" - - "github.com/uptrace/bun" -) - const ( USER_TYPE_ENT int8 = iota USER_TYPE_SUP USER_TYPE_OPS ) - -type User struct { - bun.BaseModel `bun:"table:user,alias:u"` - Created `bun:"extend"` - Id string `bun:",pk,notnull" json:"id"` - Username string `bun:",notnull" json:"username"` - Password string `bun:",notnull" json:"-"` - ResetNeeded bool `bun:",notnull" json:"resetNeeded"` - Type int8 `bun:"type:smallint,notnull" json:"type"` - Enabled bool `bun:",notnull" json:"enabled"` - Detail *UserDetail `bun:"rel:has-one,join:id=id" json:"-"` - Charges []*UserCharge `bun:"rel:has-many,join:id=user_id" json:"-"` -} - -type UserWithCredentials struct { - bun.BaseModel `bun:"table:user,alias:u"` - Created `bun:"extend"` - Id string `bun:",pk,notnull" json:"id"` - Username string `bun:",notnull" json:"username"` - Password string `bun:",notnull" json:"credential"` - ResetNeeded bool `bun:",notnull" json:"resetNeeded"` - Type int8 `bun:"type:smallint,notnull" json:"type"` - Enabled bool `bun:",notnull" json:"enabled"` -} - -var _ bun.BeforeAppendModelHook = (*User)(nil) - -func (u *User) BeforeAppendModel(ctx context.Context, query bun.Query) error { - switch query.(type) { - case *bun.InsertQuery: - u.CreatedAt = time.Now() - } - return nil -} diff --git a/model/user_charges.go b/model/user_charges.go deleted file mode 100644 index fc80c60..0000000 --- a/model/user_charges.go +++ /dev/null @@ -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 -} diff --git a/model/user_detail.go b/model/user_detail.go deleted file mode 100644 index 3cd0190..0000000 --- a/model/user_detail.go +++ /dev/null @@ -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 -} diff --git a/model/will_diluted_fee.go b/model/will_diluted_fee.go deleted file mode 100644 index fe8d641..0000000 --- a/model/will_diluted_fee.go +++ /dev/null @@ -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 -} diff --git a/router/router.go b/router/router.go index 070b4de..db53a00 100644 --- a/router/router.go +++ b/router/router.go @@ -1,7 +1,6 @@ package router import ( - "electricity_bill_calc/controller" "electricity_bill_calc/logger" "electricity_bill_calc/security" "fmt" @@ -44,18 +43,6 @@ func App() *fiber.App { })) app.Use(security.SessionRecovery) - controller.InitializeUserController(app) - controller.InitializeRegionController(app) - controller.InitializeChargesController(app) - controller.InitializeParkController(app) - controller.InitializeMaintenanceFeeController(app) - controller.InitializeMeter04kVController(app) - controller.InitializeReportController(app) - controller.InitializeEndUserController(app) - controller.InitializeWithdrawController(app) - controller.InitializeStatisticsController(app) - controller.InitializeGodModeController(app) - return app } diff --git a/service/calculate.go b/service/calculate.go deleted file mode 100644 index 6680aa4..0000000 --- a/service/calculate.go +++ /dev/null @@ -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 -} diff --git a/service/charge.go b/service/charge.go deleted file mode 100644 index d3fcead..0000000 --- a/service/charge.go +++ /dev/null @@ -1,284 +0,0 @@ -package service - -import ( - "context" - "database/sql" - "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/fufuok/utils" - "github.com/samber/lo" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _ChargeService struct { - l *zap.Logger -} - -var ChargeService = _ChargeService{ - l: logger.Named("Service", "Charge"), -} - -func (c _ChargeService) CreateChargeRecord(charge *model.UserCharge, extendWithIgnoreSettle bool) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - _, err = tx.NewInsert().Model(charge).Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - if extendWithIgnoreSettle { - err := c.updateUserExpiration(&tx, ctx, charge.UserId, charge.ChargeTo) - if err != nil { - return err - } - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation("charge") - return nil -} - -func (c _ChargeService) SettleCharge(seq int64, uid string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - var record = new(model.UserCharge) - err = tx.NewSelect().Model(&record). - Where("seq = ?", seq). - Where("user_id = ?", uid). - Scan(ctx) - if err != nil { - return nil - } - if record == nil { - return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") - } - currentTime := time.Now() - _, err = tx.NewUpdate().Model((*model.UserCharge)(nil)). - Where("seq = ?", seq). - Where("user_id = ?", uid). - Set("settled = ?", true). - Set("settled_at = ?", currentTime). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - err = c.updateUserExpiration(&tx, ctx, uid, record.ChargeTo) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) - return nil -} - -func (c _ChargeService) RefundCharge(seq int64, uid string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - currentTime := time.Now() - res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)). - Where("seq = ?", seq). - Where("user_id = ?", uid). - Set("refunded = ?", true). - Set("refunded_at = ?", currentTime). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - if rows, _ := res.RowsAffected(); rows == 0 { - tx.Rollback() - return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") - } - lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid) - if err != nil { - tx.Rollback() - return exceptions.NewNotFoundError("未找到最后合法的计费时间。") - } - err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) - return nil -} - -func (c _ChargeService) CancelCharge(seq int64, uid string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - currentTime := time.Now() - res, err := tx.NewUpdate().Model((*model.UserCharge)(nil)). - Where("seq = ?", seq). - Where("user_id = ?", uid). - Set("cancelled = ?", true). - Set("cancelled_at = ?", currentTime). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - if rows, _ := res.RowsAffected(); rows == 0 { - tx.Rollback() - return exceptions.NewNotFoundError("未找到匹配指定条件的计费记录。") - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - tx, err = global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - lastValidExpriation, err := c.lastValidChargeTo(&tx, &ctx, uid) - if err != nil { - return exceptions.NewNotFoundError("未找到最后合法的计费时间。") - } - err = c.updateUserExpiration(&tx, ctx, uid, lastValidExpriation) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation("user") - cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) - cache.AbolishRelation("charge") - cache.AbolishRelation(fmt.Sprintf("charge:%s:%d", uid, seq)) - return nil -} - -func (ch _ChargeService) updateUserExpiration(tx *bun.Tx, ctx context.Context, uid string, expiration model.Date) error { - _, err := tx.NewUpdate().Model((*model.UserDetail)(nil)). - Set("service_expiration = ?", expiration). - Where("id = ?", uid). - Exec(ctx) - if err != nil { - tx.Rollback() - } - cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) - return err -} - -func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string, page int) ([]model.ChargeWithName, int64, error) { - var ( - cond = global.DB.NewSelect() - condition = make([]string, 0) - charges = make([]model.UserCharge, 0) - ) - cond = cond.Model(&charges).Relation("Detail") - condition = append(condition, strconv.Itoa(page)) - if len(keyword) != 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("detail.name like ?", keywordCond). - WhereOr("detail.abbr like ?", keywordCond) - }) - condition = append(condition, keyword) - } - if len(beginDate) != 0 { - beginTime, err := time.ParseInLocation("2006-01-02", beginDate, time.Local) - beginTime = utils.BeginOfDay(beginTime) - if err != nil { - return make([]model.ChargeWithName, 0), 0, err - } - cond = cond.Where("c.created_at >= ?", beginTime) - condition = append(condition, strconv.FormatInt(beginTime.Unix(), 10)) - } - if len(endDate) != 0 { - endTime, err := time.ParseInLocation("2006-01-02", endDate, time.Local) - endTime = utils.EndOfDay(endTime) - if err != nil { - return make([]model.ChargeWithName, 0), 0, err - } - cond = cond.Where("c.created_at <= ?", endTime) - condition = append(condition, strconv.FormatInt(endTime.Unix(), 10)) - } - - if cachedTotal, err := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 && err == nil { - if cachedCharges, _ := cache.RetreiveSearch[[]model.ChargeWithName]("charge_with_name", condition...); cachedCharges != nil { - return *cachedCharges, cachedTotal, nil - } - } - - startItem := (page - 1) * config.ServiceSettings.ItemsPageSize - var ( - total int - err error - ) - ctx, cancel := global.TimeoutContext() - defer cancel() - total, err = cond.Limit(config.ServiceSettings.ItemsPageSize).Offset(startItem).ScanAndCount(ctx) - - relations := []string{"charge"} - chargesWithName := make([]model.ChargeWithName, 0) - for _, c := range charges { - chargesWithName = append(chargesWithName, model.ChargeWithName{ - UserCharge: c, - UserDetail: *c.Detail, - }) - relations = append(relations, fmt.Sprintf("charge:%s:%d", c.UserId, c.Seq)) - } - - cache.CacheCount(relations, "charge_with_name", int64(total), condition...) - cache.CacheSearch(chargesWithName, relations, "charge_with_name", condition...) - return chargesWithName, int64(total), err -} - -func (_ChargeService) lastValidChargeTo(tx *bun.Tx, ctx *context.Context, uid string) (model.Date, error) { - var records []model.Date - err := tx.NewSelect().Table("user_charge"). - Where("settled = ? and cancelled = ? and refunded = ? and user_id = ?", true, false, false, uid). - Column("charge_to"). - Scan(*ctx, &records) - if err != nil { - return model.NewEmptyDate(), nil - } - lastValid := lo.Reduce(records, func(acc, elem model.Date, index int) model.Date { - if elem.Time.After(acc.Time) { - return elem - } else { - return acc - } - }, model.NewEmptyDate()) - return lastValid, nil -} diff --git a/service/end_user.go b/service/end_user.go deleted file mode 100644 index 72fa950..0000000 --- a/service/end_user.go +++ /dev/null @@ -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 -} diff --git a/service/god_mode.go b/service/god_mode.go deleted file mode 100644 index 044b423..0000000 --- a/service/god_mode.go +++ /dev/null @@ -1,875 +0,0 @@ -package service - -import ( - "context" - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "fmt" - "time" - - "github.com/samber/lo" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _GodModeService struct { - l *zap.Logger -} - -var GodModeService = _GodModeService{ - l: logger.Named("Service", "GodMode"), -} - -// 从此处开始为删除报表相关的部分 - -func (_GodModeService) resetReportIndex(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var report = new(model.Report) - err := tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) - if err != nil { - tx.Rollback() - return false, exceptions.NewNotFoundError("指定报表索引未找到。") - } - report.StepState.Summary = false - report.StepState.WillDiluted = false - report.StepState.Submeter = false - report.StepState.Calculate = false - report.StepState.Preview = false - report.StepState.Publish = false - report.Published = false - report.PublishedAt = nil - report.Withdraw = model.REPORT_NOT_WITHDRAW - report.LastWithdrawAppliedAt = nil - report.LastWithdrawAuditAt = nil - - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column( - "step_state", - "published", - "published_at", - "withdraw", - "last_withdraw_applied_at", - "last_withdraw_audit_at", - ). - Exec(*ctx) - - if affected, _ := res.RowsAffected(); err != nil || affected == 0 { - tx.Rollback() - return false, err - } - return true, err -} - -func (_GodModeService) resetReportSummary(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var summary = &model.ReportSummary{ - ReportId: reportId, - } - _, err := tx.NewUpdate().Model(summary).WherePK().Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - report.StepState.Summary = false - res, err := tx.NewUpdate().Model(report). - Column("step_state"). - WherePK(). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, err -} - -func (_GodModeService) flushReportMaintenances(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - _, err := tx.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report).Where("id = ?", reportId).Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - report.StepState.WillDiluted = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, err -} - -func (g _GodModeService) resetSingleEndUserRecord(tx *bun.Tx, ctx *context.Context, record model.EndUserDetail, additionalColumns ...string) (bool, error) { - record.CurrentPeriodOverall = decimal.Zero - record.CurrentPeriodCritical = decimal.Zero - record.CurrentPeriodPeak = decimal.Zero - record.CurrentPeriodFlat = decimal.Zero - record.CurrentPeriodValley = decimal.Zero - record.AdjustOverall = decimal.Zero - record.AdjustCritical = decimal.Zero - record.AdjustPeak = decimal.Zero - record.AdjustFlat = decimal.Zero - record.AdjustValley = decimal.Zero - record.Overall = decimal.NewNullDecimal(decimal.Zero) - record.Overall.Valid = false - record.OverallFee = decimal.NewNullDecimal(decimal.Zero) - record.OverallFee.Valid = false - record.OverallProportion = decimal.Zero - record.Critical = decimal.NewNullDecimal(decimal.Zero) - record.Critical.Valid = false - record.CriticalFee = decimal.NewNullDecimal(decimal.Zero) - record.CriticalFee.Valid = false - record.Peak = decimal.NewNullDecimal(decimal.Zero) - record.Peak.Valid = false - record.PeakFee = decimal.NewNullDecimal(decimal.Zero) - record.PeakFee.Valid = false - record.Flat = decimal.NewNullDecimal(decimal.Zero) - record.Flat.Valid = false - record.FlatFee = decimal.NewNullDecimal(decimal.Zero) - record.FlatFee.Valid = false - record.Valley = decimal.NewNullDecimal(decimal.Zero) - record.Valley.Valid = false - record.ValleyFee = decimal.NewNullDecimal(decimal.Zero) - record.ValleyFee.Valid = false - record.BasicFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.BasicFeeDiluted.Valid = false - record.AdjustFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.AdjustFeeDiluted.Valid = false - record.LossDiluted = decimal.NewNullDecimal(decimal.Zero) - record.LossDiluted.Valid = false - record.LossFeeDiluted = decimal.NewNullDecimal(decimal.Zero) - record.LossFeeDiluted.Valid = false - record.FinalDiluted = decimal.NewNullDecimal(decimal.Zero) - record.FinalDiluted.Valid = false - record.FinalCharge = decimal.NewNullDecimal(decimal.Zero) - record.FinalCharge.Valid = false - - columns := []string{ - "current_period_overall", - "current_period_critical", - "current_period_peak", - "current_period_flat", - "current_period_valley", - "adjust_overall", - "adjust_critical", - "adjust_peak", - "adjust_flat", - "adjust_valley", - "overall", - "overall_fee", - "overall_proportion", - "critical", - "critical_fee", - "peak", - "peak_fee", - "flat", - "flat_fee", - "valley", - "valley_fee", - "basic_fee_diluted", - "adjust_fee_diluted", - "loss_diluted", - "loss_fee_diluted", - "maintenance_fee_diluted", - "public_consumption_diluted", - "final_diluted", - "final_charge", - } - columns = append(columns, additionalColumns...) - - _, err := tx.NewUpdate().Model(&record). - WherePK(). - Column(columns...). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - return true, nil -} - -func (g _GodModeService) resynchronizeEndUserArchives(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var currentRecords = make([]*model.EndUserDetail, 0) - err := tx.NewSelect().Model(¤tRecords). - Where("report_id = ?", reportId). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - var report = new(model.Report) - err = tx.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) - if err != nil || report == nil { - tx.Rollback() - return false, err - } - var latestArchives = make([]model.Meter04KV, 0) - err = tx.NewSelect().Model(&latestArchives). - Where("park_id = ?", report.ParkId). - Where("enabled = ?", true). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - - for _, meter := range latestArchives { - record, has := lo.Find(currentRecords, func(rec *model.EndUserDetail) bool { - return rec.ParkId == meter.ParkId && rec.MeterId == meter.Code - }) - if has { - record.CustomerName = meter.CustomerName - record.Address = meter.Address - record.Ratio = meter.Ratio - record.ContactName = meter.ContactName - record.ContactPhone = meter.ContactPhone - record.Seq = meter.Seq - record.IsPublicMeter = meter.IsPublicMeter - success, err := g.resetSingleEndUserRecord( - tx, ctx, *record, - "customer_name", - "address", - "ratio", - "contact_name", - "contact_phone", - "seq", - "public_meter", - ) - if err != nil { - return success, err - } - } else { - newEndUser := model.EndUserDetail{ - ReportId: report.Id, - ParkId: report.ParkId, - MeterId: meter.Code, - Seq: meter.Seq, - Ratio: meter.Ratio, - Address: meter.Address, - CustomerName: meter.CustomerName, - ContactName: meter.ContactName, - ContactPhone: meter.ContactPhone, - IsPublicMeter: meter.IsPublicMeter, - LastPeriodOverall: decimal.Zero, - LastPeriodCritical: decimal.Zero, - LastPeriodPeak: decimal.Zero, - LastPeriodFlat: decimal.Zero, - LastPeriodValley: decimal.Zero, - } - _, err = tx.NewInsert().Model(&newEndUser).Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - } - } - - report.StepState.Submeter = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, nil -} - -func (g _GodModeService) resetEndUserRecords(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - var records = make([]model.EndUserDetail, 0) - err := tx.NewSelect().Model(&records). - Where("report_id = ?", reportId). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - for _, u := range records { - success, err := g.resetSingleEndUserRecord(tx, ctx, u) - if err != nil { - return success, err - } - } - var report = new(model.Report) - err = tx.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - report.StepState.Submeter = false - res, err := tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(*ctx) - rows, _ := res.RowsAffected() - if err != nil { - tx.Rollback() - } - return rows >= 0, nil -} - -type ReportPeriod struct { - Id string - Period time.Time -} - -func (_GodModeService) isTheLatestReport(ctx *context.Context, reportId string) (bool, error) { - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Where("id = ?", reportId). - Scan(*ctx) - if err != nil || report == nil { - return false, exceptions.NewNotFoundErrorFromError("指定报表索引未找到,", err) - } - var maxPeriod time.Time - err = global.DB.NewSelect().Model((*model.Report)(nil)). - ColumnExpr("max(?)", bun.Ident("period")). - Where("park_id = ?", report.ParkId). - Scan(*ctx, &maxPeriod) - if err != nil { - return false, err - } - return maxPeriod.Equal(report.Period), nil -} - -func (_GodModeService) forceDeleteReport(tx *bun.Tx, ctx *context.Context, reportId string) (bool, error) { - _, err := tx.NewDelete().Model((*model.EndUserDetail)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.ReportSummary)(nil)). - Where("report_id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - _, err = tx.NewDelete().Model((*model.Report)(nil)). - Where("id = ?", reportId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, err - } - return true, nil -} - -func (g _GodModeService) ClearReportSummary(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext(12) - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.resetReportSummary(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ClearReportMaintenances(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.flushReportMaintenances(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ResynchronizeEndUser(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.resynchronizeEndUserArchives(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ResetEndUserRegisterRecords(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext(48) - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.resetEndUserRecords(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) ResetReport(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能操作非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - var result = true - r, err := g.resetEndUserRecords(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.flushReportMaintenances(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.resetReportSummary(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - r, err = g.resetReportIndex(&tx, &ctx, reportId) - if err != nil { - return false, err - } - result = result && r - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return result, nil -} - -func (g _GodModeService) DeleteReport(reportId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - isLatest, err := g.isTheLatestReport(&ctx, reportId) - if err != nil { - return false, err - } - if !isLatest { - return false, exceptions.NewImproperOperateError("不能删除非最新期数的报表。") - } - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.forceDeleteReport(&tx, &ctx, reportId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("end_user_detail") - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - - return result, nil -} - -// 从此处开始为删除园区相关的内容部分 - -func (_GodModeService) deleteSpecificMaintenance(tx *bun.Tx, ctx *context.Context, parkId, maintenanceId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)). - Where("park_id = ?", parkId). - Where("id = ?", maintenanceId). - Exec(*ctx) - - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deleteAllMaintenance(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.MaintenanceFee)(nil)). - Where("park_id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deleteAllMeters(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.Meter04KV)(nil)). - Where("park_id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (_GodModeService) deletePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - res, err := tx.NewDelete().Model((*model.Park)(nil)). - Where("id = ?", parkId). - Exec(*ctx) - if err != nil { - tx.Rollback() - return false, nil - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - return rows >= 0, err - } -} - -func (g _GodModeService) RemoveSpecificMaintenance(parkId, maintenanceId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteSpecificMaintenance(&tx, &ctx, parkId, maintenanceId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", maintenanceId)) - return result, nil -} - -func (g _GodModeService) RemoveAllMaintenance(parkId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteAllMaintenance(&tx, &ctx, parkId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("maintenance_fee") - return result, nil -} - -func (g _GodModeService) RemoveAllMeters(parkId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.deleteAllMeters(&tx, &ctx, parkId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation("meter_04kv") - return result, nil -} - -func (g _GodModeService) erasePark(tx *bun.Tx, ctx *context.Context, parkId string) (bool, error) { - var reportIds = make([]string, 0) - err := tx.NewSelect().Model((*model.Report)(nil)). - Where("park_id = ?", parkId). - Column("id"). - Scan(*ctx, &reportIds) - if err != nil { - tx.Rollback() - return false, err - } - var result = true - for _, id := range reportIds { - r, err := g.forceDeleteReport(tx, ctx, id) - if err != nil { - return false, err - } - result = result && r - } - r, err := g.deleteAllMaintenance(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - r, err = g.deleteAllMeters(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - r, err = g.deletePark(tx, ctx, parkId) - if err != nil { - return false, err - } - result = result && r - - return result, err -} - -func (g _GodModeService) RemovePark(parkId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - result, err := g.erasePark(&tx, &ctx, parkId) - if err != nil { - return false, err - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("park:%s", parkId)) - return result, nil -} - -// 从此处开始为删除用户相关的部分 - -func (g _GodModeService) DeleteUser(userId string) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - - var parkIds = make([]string, 0) - err = tx.NewSelect().Model((*model.Park)(nil)). - Where("user_id = ?", userId). - WhereAllWithDeleted(). - Column("id"). - Scan(ctx, &parkIds) - if err != nil { - tx.Rollback() - return false, err - } - - var result = true - for _, p := range parkIds { - r, err := g.erasePark(&tx, &ctx, p) - if err != nil { - return false, err - } - result = result && r - } - - // 删除用户服务计费数据。 - res, err := tx.NewDelete().Model((*model.UserCharge)(nil)). - Where("user_id = ?", userId). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - // 删除用户详细信息数据 - res, err = tx.NewDelete().Model((*model.UserDetail)(nil)). - Where("id = ?", userId). - ForceDelete(). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - // 删除用户基本索引数据 - res, err = tx.NewDelete().Model((*model.User)(nil)). - Where("id = ?", userId). - Exec(ctx) - if err != nil { - tx.Rollback() - return false, err - } - if rows, err := res.RowsAffected(); err != nil { - tx.Rollback() - return false, err - } else { - result = result && (rows >= 0) - } - - err = tx.Commit() - if err != nil { - tx.Rollback() - return false, err - } - cache.AbolishRelation(fmt.Sprintf("user:%s", userId)) - cache.AbolishRelation("user") - cache.AbolishRelation("park") - cache.AbolishRelation("report") - cache.AbolishRelation("charge") - return result, nil -} diff --git a/service/maintenance_fee.go b/service/maintenance_fee.go deleted file mode 100644 index e4453d5..0000000 --- a/service/maintenance_fee.go +++ /dev/null @@ -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 -} diff --git a/service/meter04kv.go b/service/meter04kv.go deleted file mode 100644 index 04c901a..0000000 --- a/service/meter04kv.go +++ /dev/null @@ -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 -} diff --git a/service/park.go b/service/park.go deleted file mode 100644 index ab58527..0000000 --- a/service/park.go +++ /dev/null @@ -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 -} diff --git a/service/region.go b/service/region.go deleted file mode 100644 index 4e6f0ff..0000000 --- a/service/region.go +++ /dev/null @@ -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 -} diff --git a/service/report.go b/service/report.go deleted file mode 100644 index da5043d..0000000 --- a/service/report.go +++ /dev/null @@ -1,742 +0,0 @@ -package service - -import ( - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "electricity_bill_calc/tools" - "fmt" - "strconv" - "time" - - "github.com/fufuok/utils" - "github.com/google/uuid" - "github.com/samber/lo" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _ReportService struct { - l *zap.Logger -} - -var ReportService = _ReportService{ - l: logger.Named("Service", "Report"), -} - -func (_ReportService) FetchParksWithNewestReport(uid string) ([]model.ParkNewestReport, error) { - if cachedParks, _ := cache.RetreiveSearch[[]model.ParkNewestReport]("park_newest_report", uid); cachedParks != nil { - return *cachedParks, nil - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - parks := make([]model.Park, 0) - err := global.DB.NewSelect().Model(&parks).Relation("Reports"). - Where("user_id = ?", uid). - Where("enabled = ?", true). - Order("created_at asc"). - Scan(ctx) - if err != nil { - return make([]model.ParkNewestReport, 0), err - } - - reducedParks := lo.Reduce( - parks, - func(acc map[string]model.ParkNewestReport, elem model.Park, index int) map[string]model.ParkNewestReport { - if _, ok := acc[elem.Id]; !ok { - newestReport := lo.MaxBy(elem.Reports, func(a, b *model.Report) bool { - return a.Period.After(b.Period) - }) - acc[elem.Id] = model.ParkNewestReport{ - Report: newestReport, - Park: elem, - } - } - return acc - }, - make(map[string]model.ParkNewestReport, 0), - ) - relations := lo.Map(parks, func(r model.Park, _ int) string { - return fmt.Sprintf("park:%s", r.Id) - }) - relations = append(relations, "park", "report") - cache.CacheSearch(reducedParks, relations, "park_newest_report", uid) - return lo.Values(reducedParks), nil -} - -func (_ReportService) IsNewPeriodValid(uid, pid string, period time.Time) (bool, error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - reports := make([]model.Report, 0) - if cachedReport, _ := cache.RetreiveSearch[[]model.Report]("report", "user", uid, "park", pid); cachedReport != nil { - reports = *cachedReport - } else { - err := global.DB.NewSelect().Model(&reports).Relation("Park"). - Where("park.user_id = ?", uid). - Where("r.park_id = ?", pid). - Scan(ctx) - if err != nil { - return false, err - } - cache.CacheSearch(reports, []string{"report", "park"}, "park", "user", uid, "park", pid) - } - // 检查给定的期数在目前的记录中是否已经存在 - exists := lo.Reduce( - reports, - func(acc bool, elem model.Report, index int) bool { - if elem.Period.Equal(period) { - return acc || true - } else { - return acc || false - } - }, - false, - ) - if exists { - return false, nil - } - // 检查给定的期数与目前已发布的最大期数的关系 - maxPublished := lo.Reduce( - reports, - func(acc *time.Time, elem model.Report, index int) *time.Time { - if elem.Published { - if acc == nil || (acc != nil && elem.Period.After(*acc)) { - return &elem.Period - } - } - return acc - }, - nil, - ) - // 检查给定的期数与目前未发布的最大期数的关系 - maxUnpublished := lo.Reduce( - reports, - func(acc *time.Time, elem model.Report, index int) *time.Time { - if acc == nil || (acc != nil && elem.Period.After(*acc)) { - return &elem.Period - } - return acc - }, - nil, - ) - if maxUnpublished == nil { - return true, nil - } - if maxPublished != nil && maxUnpublished.Equal(*maxPublished) { - // 此时不存在未发布的报表 - return tools.IsNextMonth(*maxPublished, period), nil - } else { - // 存在未发布的报表 - return false, nil - } -} - -func (_ReportService) InitializeNewReport(parkId string, period time.Time) (string, error) { - ctx, cancel := global.TimeoutContext(120) - defer cancel() - - periods := make([]model.Report, 0) - err := global.DB.NewSelect().Model(&periods). - Where("park_id = ?", parkId). - Where("published = ?", true). - Order("period asc"). - Scan(ctx) - if err != nil { - return "", err - } - // 获取上一期的报表索引信息 - maxPublishedReport := lo.Reduce( - periods, - func(acc *model.Report, elem model.Report, index int) *model.Report { - if acc == nil || (acc != nil && elem.Period.After(acc.Period)) { - return &elem - } - return acc - }, - nil, - ) - var indexedLastPeriodCustomers map[string]model.EndUserDetail - if maxPublishedReport != nil { - // 获取上一期的所有户表信息,并获取当前已启用的所有用户 - lastPeriodCustomers := make([]model.EndUserDetail, 0) - err = global.DB.NewSelect().Model(&lastPeriodCustomers). - Where("report_id = ?", maxPublishedReport.Id). - Scan(ctx) - if err != nil { - return "", err - } - indexedLastPeriodCustomers = lo.Reduce( - lastPeriodCustomers, - 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), - ) - } else { - indexedLastPeriodCustomers = make(map[string]model.EndUserDetail, 0) - } - currentActivatedCustomers := make([]model.Meter04KV, 0) - err = global.DB.NewSelect().Model(¤tActivatedCustomers). - Where("park_id = ?", parkId). - Where("enabled = ?", true). - Scan(ctx) - if err != nil { - return "", err - } - var parkInfo = new(model.Park) - err = global.DB.NewSelect().Model(parkInfo). - Where("id = ?", parkId). - Scan(ctx) - if err != nil || parkInfo == nil { - return "", exceptions.NewNotFoundError(fmt.Sprintf("指定园区未找到, %v", err)) - } - // 生成新一期的报表 - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return "", err - } - // 插入已经生成的报表索引信息和园区概况信息 - newReport := model.Report{ - Id: uuid.New().String(), - ParkId: parkId, - Period: period, - Category: parkInfo.Category, - SubmeterType: parkInfo.SubmeterType, - StepState: model.NewSteps(), - Published: false, - Withdraw: model.REPORT_NOT_WITHDRAW, - } - newReportSummary := model.ReportSummary{ - ReportId: newReport.Id, - } - _, err = tx.NewInsert().Model(&newReport).Exec(ctx) - if err != nil { - tx.Rollback() - return "", err - } - _, err = tx.NewInsert().Model(&newReportSummary).Exec(ctx) - if err != nil { - tx.Rollback() - return "", err - } - // 生成并插入户表信息 - var inserts = make([]model.EndUserDetail, 0) - for _, customer := range currentActivatedCustomers { - newEndUser := model.EndUserDetail{ - ReportId: newReport.Id, - ParkId: parkId, - MeterId: customer.Code, - Seq: customer.Seq, - Ratio: customer.Ratio, - Address: customer.Address, - CustomerName: customer.CustomerName, - ContactName: customer.ContactName, - ContactPhone: customer.ContactPhone, - IsPublicMeter: customer.IsPublicMeter, - LastPeriodOverall: decimal.Zero, - LastPeriodCritical: decimal.Zero, - LastPeriodPeak: decimal.Zero, - LastPeriodFlat: decimal.Zero, - LastPeriodValley: decimal.Zero, - } - if lastPeriod, ok := indexedLastPeriodCustomers[customer.Code]; ok { - newEndUser.LastPeriodOverall = lastPeriod.CurrentPeriodOverall - newEndUser.LastPeriodCritical = lastPeriod.CurrentPeriodCritical - newEndUser.LastPeriodPeak = lastPeriod.CurrentPeriodPeak - newEndUser.LastPeriodFlat = lastPeriod.CurrentPeriodFlat - newEndUser.LastPeriodValley = lastPeriod.CurrentPeriodValley - } - inserts = append(inserts, newEndUser) - } - 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("report") - return newReport.Id, nil -} - -func (_ReportService) RetreiveReportIndex(rid string) (*model.Report, error) { - if cachedReport, _ := cache.RetreiveEntity[model.Report]("report", rid); cachedReport != nil { - return cachedReport, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Where("id = ?", rid). - Scan(ctx) - if err != nil { - return nil, err - } - cache.CacheEntity(report, []string{fmt.Sprintf("report:%s", rid), "park"}, "report", rid) - return report, nil -} - -func (_ReportService) RetreiveReportSummary(rid string) (*model.ReportSummary, error) { - if cachedSummary, _ := cache.RetreiveEntity[model.ReportSummary]("report_summary", rid); cachedSummary != nil { - return cachedSummary, nil - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - var summary = new(model.ReportSummary) - err := global.DB.NewSelect().Model(summary). - Where("report_id = ?", rid). - Scan(ctx) - if err != nil { - return nil, err - } - cache.CacheEntity(summary, []string{fmt.Sprintf("report:%s", rid), "park"}, "report_summary", rid) - return summary, nil -} - -func (_ReportService) UpdateReportSummary(summary *model.ReportSummary) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - _, err := global.DB.NewUpdate().Model(summary). - WherePK(). - Column("overall", "overall_fee", "critical", "critical_fee", "peak", "peak_fee", "valley", "valley_fee", "basic_fee", "adjust_fee"). - Exec(ctx) - if err == nil { - cache.AbolishRelation(fmt.Sprintf("report:%s", summary.ReportId)) - } - return err -} - -func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - var report = new(model.Report) - err := global.DB.NewSelect().Model(report).Relation("Summary"). - Where("r.id = ?", reportId). - Scan(ctx) - if err != nil || report == nil { - return exceptions.NewNotFoundErrorFromError("未找到指定报表", err) - } - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - - report.Summary.CalculatePrices() - _, err = tx.NewUpdate().Model(report.Summary). - WherePK(). - Column("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price", "consumption_fee"). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - report.StepState.Summary = true - _, err = tx.NewUpdate().Model(report). - WherePK(). - Column("step_state"). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation(fmt.Sprintf("report:%s", reportId)) - return nil -} - -func (_ReportService) FetchWillDulutedMaintenanceFees(reportId string) ([]model.WillDilutedFee, error) { - if cachedFees, _ := cache.RetreiveSearch[[]model.WillDilutedFee]("will_diluted_fee", "report", reportId); cachedFees != nil { - return *cachedFees, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - fees := make([]model.WillDilutedFee, 0) - err := global.DB.NewSelect().Model(&fees). - Where("report_id = ?", reportId). - Order("created_at asc"). - Scan(ctx) - if err != nil { - return make([]model.WillDilutedFee, 0), nil - } - relations := lo.Map(fees, func(f model.WillDilutedFee, _ int) string { - return fmt.Sprintf("will_diluted_fee:%s", f.Id) - }) - relations = append(relations, fmt.Sprintf("report:will_diluted_fee:%s", reportId), fmt.Sprintf("report:%s", reportId), "park") - cache.CacheSearch(fees, relations, "will_diluted_fee", "report", reportId) - return fees, nil -} - -func (_ReportService) CreateTemporaryWillDilutedMaintenanceFee(fee model.WillDilutedFee) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - fee.Id = utils.UUIDString() - _, err := global.DB.NewInsert().Model(&fee).Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("report:will_diluted_fee:%s", fee.ReportId)) - return err -} - -func (_ReportService) BatchSaveMaintenanceFee(reportId string, fees []model.WillDilutedFee) error { - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - // 首先删除所有预定义的部分,条件是指定报表ID,SourceID不为空。 - _, err = tx.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("report_id = ?", reportId). - Where("source_id is not null"). - Exec(ctx) - if err != nil { - tx.Rollback() - return err - } - // 然后插入新的记录 - _, err = tx.NewInsert().Model(&fees).Exec(ctx) - if err != nil { - return err - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return err - } - cache.AbolishRelation(fmt.Sprintf("report:will_diluted_fee:%s", reportId)) - return nil -} - -func (_ReportService) UpdateMaintenanceFee(feeId string, updates map[string]interface{}) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - updates["last_modified_at"] = lo.ToPtr(time.Now()) - _, err = global.DB.NewUpdate().Model(&updates).TableExpr("will_diluted_fee"). - Where("id = ?", feeId). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("will_diluted_fee:%s", feeId)) - return -} - -func (_ReportService) DeleteWillDilutedFee(fee string) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - _, err = global.DB.NewDelete().Model((*model.WillDilutedFee)(nil)). - Where("id = ?", fee). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("will_diluted_fee:%s", fee)) - return -} - -func (_ReportService) ProgressReportWillDilutedFee(report model.Report) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - report.StepState.WillDiluted = true - _, err = global.DB.NewUpdate().Model(&report). - WherePK(). - Column("step_state"). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id)) - return -} - -func (_ReportService) ProgressReportRegisterEndUser(report model.Report) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - report.StepState.Submeter = true - _, err = global.DB.NewUpdate().Model(&report). - WherePK(). - Column("step_state"). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id)) - return -} - -func (_ReportService) ProgressReportCalculate(report model.Report) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - report.StepState.Calculate = true - _, err = global.DB.NewUpdate().Model(&report). - WherePK(). - Column("step_state"). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id)) - return -} - -func (_ReportService) RetreiveParkEndUserMeterType(reportId string) (int, error) { - if cachedType, _ := cache.RetreiveEntity[int]("park_end_user_meter_type", fmt.Sprintf("report_%s", reportId)); cachedType != nil { - return *cachedType, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - var mType int - err := global.DB.NewSelect().Model((*model.Report)(nil)). - Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Column("meter_04kv_type") - }). - ExcludeColumn("*"). - Where("r.id = ?", reportId). - Scan(ctx, &mType) - if err != nil { - return -1, err - } - cache.CacheEntity(mType, []string{fmt.Sprintf("report:%s", reportId), "park"}, "park_end_user_meter_type", fmt.Sprintf("report_%s", reportId)) - return mType, nil -} - -func (_ReportService) PublishReport(report model.Report) (err error) { - ctx, cancel := global.TimeoutContext() - defer cancel() - - report.Published = true - report.PublishedAt = lo.ToPtr(time.Now()) - report.StepState.Publish = true - _, err = global.DB.NewUpdate().Model(&report). - WherePK(). - Column("step_state", "published", "published_at"). - Exec(ctx) - cache.AbolishRelation(fmt.Sprintf("report:%s", report.Id)) - return -} - -func (_ReportService) SearchReport(requestUser, requestPark, requestKeyword string, requestPeriod *time.Time, requestPage int, onlyPublished bool) ([]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(requestPage)) - if onlyPublished { - cond = cond.Where("r.published = ?", true) - } - conditions = append(conditions, strconv.FormatBool(onlyPublished)) - if len(requestUser) > 0 { - cond = cond.Where("park.user_id = ?", requestUser) - conditions = append(conditions, requestUser) - } - if len(requestPark) > 0 { - cond = cond.Where("park.id = ?", requestPark) - conditions = append(conditions, requestPark) - } - if requestPeriod != nil { - cond = cond.Where("r.period = ?", *requestPeriod) - conditions = append(conditions, strconv.FormatInt(requestPeriod.Unix(), 10)) - } - if len(requestKeyword) > 0 { - keywordCond := "%" + requestKeyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("park.name like ?", keywordCond). - WhereOr("park__enterprise.name like ?", keywordCond). - WhereOr("park__enterprise.abbr like ?", keywordCond). - WhereOr("park.abbr like ?", keywordCond). - WhereOr("park__enterprise.address like ?", keywordCond). - WhereOr("park.address like ?", keywordCond) - }) - conditions = append(conditions, requestKeyword) - } - if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil { - if cachedRecords, _ := cache.RetreiveSearch[[]model.JoinedReportForWithdraw]("join_report_for_withdraw", conditions...); cachedRecords != nil { - return *cachedRecords, cachedTotal, nil - } - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize - - total, err := cond.Limit(config.ServiceSettings.ItemsPageSize). - Offset(startItem). - ScanAndCount(ctx) - - records := make([]model.JoinedReportForWithdraw, 0) - relations := []string{"report", "park"} - for _, r := range reports { - records = append(records, model.JoinedReportForWithdraw{ - Report: r, - Park: model.FromPark(*r.Park), - User: model.FromUserDetail(*r.Park.Enterprise), - }) - relations = append(relations, fmt.Sprintf("report:%s", r.Id)) - } - - cache.CacheCount(relations, "join_report_for_withdraw", int64(total), conditions...) - cache.CacheSearch(records, relations, "join_report_for_withdraw", conditions...) - return records, int64(total), err -} - -func (_ReportService) AssembleReportPublicity(reportId string) (*model.Publicity, error) { - if cachedPublicity, _ := cache.RetreiveEntity[model.Publicity]("publicity", reportId); cachedPublicity != nil { - return cachedPublicity, nil - } - // 资料准备 - ctx, cancel := global.TimeoutContext() - defer cancel() - - var report = new(model.Report) - err := global.DB.NewSelect().Model(report). - Relation("Summary").Relation("WillDilutedFees").Relation("EndUsers"). - Relation("Park").Relation("Park.Enterprise"). - Where("r.id = ?", reportId). - Scan(ctx) - if err != nil { - return nil, exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) - } - - // 组合数据 - paidPart := model.PaidPart{ - Overall: report.Summary.Overall, - OverallPrice: report.Summary.OverallPrice.Decimal, - ConsumptionFee: report.Summary.ConsumptionFee.Decimal, - OverallFee: report.Summary.OverallFee, - Critical: decimal.NewNullDecimal(report.Summary.Critical), - CriticalPrice: report.Summary.CriticalPrice, - CriticalFee: decimal.NewNullDecimal(report.Summary.CriticalFee), - Peak: decimal.NewNullDecimal(report.Summary.Peak), - PeakPrice: report.Summary.PeakPrice, - PeakFee: decimal.NewNullDecimal(report.Summary.PeakFee), - Flat: decimal.NewNullDecimal(report.Summary.Flat), - FlatPrice: report.Summary.FlatPrice, - FlatFee: decimal.NewNullDecimal(report.Summary.FlatFee), - Valley: decimal.NewNullDecimal(report.Summary.Valley), - ValleyPrice: report.Summary.ValleyPrice, - ValleyFee: decimal.NewNullDecimal(report.Summary.ValleyFee), - BasicFee: report.Summary.BasicFee, - AdjustFee: report.Summary.AdjustFee, - } - endUserSummary := model.ConsumptionOverallPart{ - Overall: report.Summary.Customers.Consumption.Decimal, - OverallPrice: report.Summary.OverallPrice.Decimal, - ConsumptionFee: report.Summary.Customers.ConsumptionFee.Decimal, - OverallFee: report.Summary.Customers.ConsumptionFee.Decimal, - Critical: report.Summary.Customers.Critical, - CriticalPrice: report.Summary.CriticalPrice, - CriticalFee: report.Summary.Customers.CriticalFee, - Peak: report.Summary.Customers.Peak, - PeakPrice: report.Summary.PeakPrice, - PeakFee: report.Summary.Customers.PeakFee, - Flat: report.Summary.Customers.Flat, - FlatPrice: report.Summary.FlatPrice, - FlatFee: report.Summary.Customers.FlatFee, - Valley: report.Summary.Customers.Valley, - ValleyPrice: report.Summary.ValleyPrice, - ValleyFee: report.Summary.Customers.ValleyFee, - Proportion: report.Summary.Customers.Proportion.Decimal, - } - lossPart := model.LossPart{ - Quantity: report.Summary.Loss.Decimal, - Price: report.Summary.OverallPrice.Decimal, - ConsumptionFee: report.Summary.LossFee.Decimal, - Proportion: report.Summary.LossProportion.Decimal, - AuthorizeQuantity: report.Summary.AuthorizeLoss.Decimal, - AuthorizeConsumptionFee: report.Summary.AuthorizeLossFee.Decimal, - } - publicSummary := model.ConsumptionOverallPart{ - Overall: report.Summary.Publics.Consumption.Decimal, - OverallPrice: report.Summary.OverallPrice.Decimal, - ConsumptionFee: report.Summary.Publics.ConsumptionFee.Decimal, - OverallFee: report.Summary.Publics.ConsumptionFee.Decimal, - Critical: report.Summary.Publics.Critical, - CriticalPrice: report.Summary.CriticalPrice, - CriticalFee: report.Summary.Publics.CriticalFee, - Peak: report.Summary.Publics.Peak, - PeakPrice: report.Summary.PeakPrice, - PeakFee: report.Summary.Publics.PeakFee, - Flat: report.Summary.Publics.Flat, - FlatPrice: report.Summary.FlatPrice, - FlatFee: report.Summary.Publics.FlatFee, - Valley: report.Summary.Publics.Valley, - ValleyPrice: report.Summary.ValleyPrice, - ValleyFee: report.Summary.Publics.ValleyFee, - Proportion: report.Summary.Publics.Proportion.Decimal, - } - otherCollection := model.OtherShouldCollectionPart{ - LossFee: report.Summary.AuthorizeLossFee, - BasicFees: report.Summary.BasicFee.Add(report.Summary.AdjustFee), - } - finalAdjustFee := lossPart.AuthorizeConsumptionFee.Add(otherCollection.BasicFees) - var adjustPrice = decimal.Zero - if !endUserSummary.Overall.Equal(decimal.Zero) { - adjustPrice = finalAdjustFee.Div(endUserSummary.Overall).RoundBank(8) - } - var adjustProportion = decimal.Zero - if !paidPart.OverallFee.Equal(decimal.Zero) { - adjustProportion = finalAdjustFee.Div(paidPart.OverallFee.Add(finalAdjustFee)).RoundBank(8) - } - maintenanceFees := model.MaintenancePart{ - BasicFees: otherCollection.BasicFees, - LossFee: lossPart.AuthorizeConsumptionFee, - AdjustFee: finalAdjustFee, - LossProportion: lossPart.Proportion, - AdjustPrice: adjustPrice, - AdjustProportion: adjustProportion, - } - if maintenanceFees.LossProportion.GreaterThan(decimal.NewFromFloat(0.1)) { - maintenanceFees.LossProportion = decimal.NewFromFloat(0.1) - } - endUsers := lo.Map( - report.EndUsers, - func(elem *model.EndUserDetail, index int) model.EndUserSummary { - return model.EndUserSummary{ - CustomerName: elem.CustomerName, - Address: elem.Address, - MeterId: elem.MeterId, - IsPublicMeter: elem.IsPublicMeter, - Overall: elem.Overall.Decimal, - OverallPrice: report.Summary.OverallPrice.Decimal, - OverallFee: elem.OverallFee.Decimal, - Critical: elem.Critical, - CriticalFee: elem.CriticalFee, - Peak: elem.Peak, - PeakFee: elem.PeakFee, - Valley: elem.Valley, - ValleyFee: elem.ValleyFee, - Loss: elem.LossDiluted.Decimal, - LossFee: elem.LossFeeDiluted.Decimal, - } - }, - ) - - publicity := &model.Publicity{ - Report: *report, - Park: *report.Park, - User: *report.Park.Enterprise, - Paid: paidPart, - EndUser: endUserSummary, - Loss: lossPart, - PublicConsumptionOverall: publicSummary, - OtherCollections: otherCollection, - Maintenance: maintenanceFees, - EndUserDetails: endUsers, - } - cache.CacheEntity(publicity, []string{fmt.Sprintf("publicity:%s", reportId), fmt.Sprintf("report:%s", reportId), "report", "park"}, "publicity", reportId) - - return publicity, nil -} diff --git a/service/statistics.go b/service/statistics.go deleted file mode 100644 index d500881..0000000 --- a/service/statistics.go +++ /dev/null @@ -1,94 +0,0 @@ -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" - "go.uber.org/zap" -) - -type _StatisticsService struct { - l *zap.Logger -} - -var StatisticsService = _StatisticsService{ - l: logger.Named("Service", "Stat"), -} - -func (_StatisticsService) EnabledEnterprises() (int64, error) { - if cachedCount, err := cache.RetreiveCount("enabled_ent"); cachedCount != -1 && err == nil { - return cachedCount, nil - } - 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 -} - -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) - if err != nil { - return make([]model.ParkPeriodStatistics, 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 -} diff --git a/service/user.go b/service/user.go deleted file mode 100644 index bc28c04..0000000 --- a/service/user.go +++ /dev/null @@ -1,447 +0,0 @@ -package service - -import ( - "database/sql" - "electricity_bill_calc/cache" - "electricity_bill_calc/config" - "electricity_bill_calc/exceptions" - "electricity_bill_calc/global" - "electricity_bill_calc/logger" - "electricity_bill_calc/model" - "electricity_bill_calc/tools" - "fmt" - "strconv" - "time" - - "github.com/fufuok/utils" - "github.com/google/uuid" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _UserService struct { - l *zap.Logger -} - -var UserService = _UserService{ - l: logger.Named("Service", "User"), -} - -func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*model.Session, error) { - user, err := u.findUserWithCredentialsByUsername(username) - - if err != nil { - return nil, err - } - if user == nil { - return nil, exceptions.NewAuthenticationError(404, "用户不存在。") - } - if user.Type != 0 { - return nil, exceptions.NewAuthenticationError(400, "用户类型不正确。") - } - if !user.Enabled { - return nil, exceptions.NewAuthenticationError(403, "用户已被禁用。") - } - hashedPassword := utils.Sha512Hex(password) - if hashedPassword != user.Password { - return nil, exceptions.NewAuthenticationError(402, "用户凭据不正确。") - } - if user.ResetNeeded { - authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") - authErr.NeedReset = true - return nil, authErr - } - userDetial, _ := u.retreiveUserDetail(user.Id) - if userDetial.ServiceExpiration.Time.Before(time.Now()) { - return nil, exceptions.NewAuthenticationError(406, "用户服务期限已过。") - } - session := &model.Session{ - Token: uuid.New().String(), - Uid: user.Id, - Type: user.Type, - Name: user.Username, - ExpiresAt: time.Now().Add(config.ServiceSettings.MaxSessionLife), - } - if userDetial != nil { - session.Name = *userDetial.Name - } - cache.CacheSession(session) - return session, nil -} - -func (u _UserService) ProcessManagementUserLogin(username, password string) (*model.Session, error) { - user, err := u.findUserWithCredentialsByUsername(username) - - if err != nil { - return nil, err - } - if user == nil { - return nil, exceptions.NewAuthenticationError(404, "用户不存在。") - } - if user.Type != 1 && user.Type != 2 { - return nil, exceptions.NewAuthenticationError(400, "用户类型不正确。") - } - if !user.Enabled { - return nil, exceptions.NewAuthenticationError(403, "用户已被禁用。") - } - hashedPassword := utils.Sha512Hex(password) - if hashedPassword != user.Password { - return nil, exceptions.NewAuthenticationError(402, "用户凭据不正确。") - } - if user.ResetNeeded { - authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") - authErr.NeedReset = true - return nil, authErr - } - session := &model.Session{ - Token: uuid.New().String(), - Uid: user.Id, - Type: user.Type, - Name: user.Username, - ExpiresAt: time.Now().Add(config.ServiceSettings.MaxSessionLife), - } - userDetial, _ := u.retreiveUserDetail(user.Id) - if userDetial != nil { - session.Name = *userDetial.Name - } - cache.CacheSession(session) - return session, nil -} - -func (u _UserService) InvalidUserPassword(uid string) (string, error) { - user, err := u.findUserByID(uid) - if user == nil && err != nil { - return "", exceptions.NewNotFoundError("指定的用户不存在。") - } - ctx, cancel := global.TimeoutContext() - defer cancel() - verifyCode := tools.RandStr(10) - user.Password = utils.Sha512Hex(verifyCode) - user.ResetNeeded = true - res, err := global.DB.NewUpdate().Model(user).WherePK().Column("password", "reset_needed").Exec(ctx) - if err != nil { - return "", err - } - if affected, _ := res.RowsAffected(); affected > 0 { - // ! 清除与此用户所有相关的记录。 - cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) - return verifyCode, nil - } else { - return "", exceptions.NewUnsuccessfulOperationError() - } -} - -func (u _UserService) VerifyUserPassword(username, verifyCode string) (bool, error) { - user, err := u.findUserByUsername(username) - if user == nil || err != nil { - return false, exceptions.NewNotFoundError("指定的用户不存在。") - } - hashedVerifyCode := utils.Sha512Hex(verifyCode) - if hashedVerifyCode != user.Password { - return false, nil - } else { - return true, nil - } -} - -func (u _UserService) ResetUserPassword(username, password string) (bool, error) { - user, err := u.findUserByUsername(username) - if user == nil || err != nil { - return false, exceptions.NewNotFoundError("指定的用户不存在。") - } - ctx, cancel := global.TimeoutContext() - defer cancel() - user.Password = utils.Sha512Hex(password) - user.ResetNeeded = false - res, err := global.DB.NewUpdate().Model(user).WherePK().Column("password", "reset_needed").Exec(ctx) - if err != nil { - return false, err - } - if affected, _ := res.RowsAffected(); affected > 0 { - cache.AbolishRelation(fmt.Sprintf("user:%s", user.Id)) - return true, nil - } else { - return false, nil - } -} - -func (_UserService) IsUserExists(uid string) (bool, error) { - if has, _ := cache.CheckExists("user", uid); has { - return has, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - has, err := global.DB.NewSelect().Model((*model.User)(nil)).Where("id = ?", uid).Exists(ctx) - if has { - cache.CacheExists([]string{"user", fmt.Sprintf("user_%s", uid)}, "user", uid) - } - return has, err -} - -func (_UserService) IsUsernameExists(username string) (bool, error) { - if has, _ := cache.CheckExists("user", username); has { - return has, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - has, err := global.DB.NewSelect().Model((*model.User)(nil)).Where("username = ?", username).Exists(ctx) - if has { - cache.CacheExists([]string{"user"}, "user", username) - } - return has, err -} - -func (u _UserService) CreateUser(user *model.User, detail *model.UserDetail) (string, error) { - if len(user.Id) == 0 { - user.Id = uuid.New().String() - } - exists, err := u.IsUserExists(user.Id) - if exists { - return "", exceptions.NewNotFoundError("user already exists") - } - if err != nil { - return "", nil - } - detail.Id = user.Id - - verifyCode := tools.RandStr(10) - user.Password = utils.Sha512Hex(verifyCode) - user.ResetNeeded = true - - if detail.Name != nil { - finalAbbr := tools.PinyinAbbr(*detail.Name) - detail.Abbr = &finalAbbr - } - ctx, cancel := global.TimeoutContext() - defer cancel() - - tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return "", err - } - _, err = tx.NewInsert().Model(user).Exec(ctx) - if err != nil { - tx.Rollback() - return "", fmt.Errorf("user create failed: %w", err) - } - _, err = tx.NewInsert().Model(detail).Exec(ctx) - if err != nil { - tx.Rollback() - return "", fmt.Errorf("user Detail create failed: %w", err) - } - err = tx.Commit() - if err != nil { - tx.Rollback() - return "", fmt.Errorf("transaction commit unsuccessful: %w", err) - } - // ! 广谱关联关系的废除必须是在有新记录加入或者有记录被删除的情况下。 - cache.AbolishRelation("user") - return verifyCode, nil -} - -func (u _UserService) SwitchUserState(uid string, enabled bool) error { - exists, err := u.IsUserExists(uid) - if !exists { - return exceptions.NewNotFoundError("user not exists") - } - if err != nil { - return err - } - newStateUser := new(model.User) - newStateUser.Id = uid - newStateUser.Enabled = enabled - ctx, cancel := global.TimeoutContext() - defer cancel() - res, err := global.DB.NewUpdate().Model(newStateUser).WherePK().Column("enabled").Exec(ctx) - if affected, _ := res.RowsAffected(); err == nil && affected > 0 { - cache.AbolishRelation(fmt.Sprintf("user:%s", uid)) - } - return err -} - -func (us _UserService) SearchLimitUsers(keyword string, limit int) ([]model.JoinedUserDetail, error) { - if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", keyword, strconv.Itoa(limit)); cachedUsers != nil { - return *cachedUsers, nil - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - - var users = make([]model.User, 0) - keywordCond := "%" + keyword + "%" - err := global.DB.NewSelect().Model(&users).Relation("Detail"). - Where("u.type = ?", model.USER_TYPE_ENT). - WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("u.username like ?", keywordCond). - WhereOr("detail.name like ?", keywordCond). - WhereOr("detail.abbr like ?", keywordCond). - WhereOr("detail.contact like ?", keywordCond). - WhereOr("detail.address like ?", keywordCond) - }). - Order("u.created_at asc"). - Limit(limit). - Offset(0). - Scan(ctx) - if err != nil { - return make([]model.JoinedUserDetail, 0), err - } - var detailedUsers = make([]model.JoinedUserDetail, 0) - var relations = make([]string, 0) - // ! 这里的转换是为了兼容之前使用Xorm时构建的关联关系而存在的 - for _, u := range users { - detailedUsers = append(detailedUsers, model.JoinedUserDetail{ - UserDetail: *u.Detail, - Id: u.Id, - Username: u.Username, - Type: u.Type, - Enabled: u.Enabled, - }) - relations = append(relations, fmt.Sprintf("user:%s", u.Id)) - } - relations = append(relations, "user") - cache.CacheSearch(detailedUsers, relations, "join_user_detail", keyword, strconv.Itoa(limit)) - return detailedUsers, nil -} - -func (_UserService) findUserWithCredentialsByUsername(username string) (*model.UserWithCredentials, error) { - if cachedUser, _ := cache.RetreiveSearch[model.UserWithCredentials]("user_with_credentials", username); cachedUser != nil { - return cachedUser, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - user := new(model.UserWithCredentials) - err := global.DB.NewSelect().Model(user).Where("username = ?", username).Scan(ctx) - if err == nil { - cache.CacheSearch(*user, []string{fmt.Sprintf("user:%s", user.Id)}, "user_with_credentials", username) - } - return user, err -} - -func (u _UserService) findUserByUsername(username string) (*model.User, error) { - if cachedUser, _ := cache.RetreiveSearch[model.User]("user", username); cachedUser != nil { - return cachedUser, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - user := new(model.User) - err := global.DB.NewSelect().Model(user).Where("username = ?", username).Scan(ctx) - if err == nil { - cache.CacheSearch(*user, []string{fmt.Sprintf("user:%s", user.Id)}, "user", username) - } - return user, err -} - -func (_UserService) retreiveUserDetail(uid string) (*model.UserDetail, error) { - if cachedUser, _ := cache.RetreiveEntity[model.UserDetail]("user_detail", uid); cachedUser != nil { - return cachedUser, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - user := &model.UserDetail{ - Id: uid, - } - err := global.DB.NewSelect().Model(user).WherePK().Scan(ctx) - if err == nil { - cache.CacheEntity(*user, []string{fmt.Sprintf("user:%s", uid)}, "user_detail", uid) - } - return user, err -} - -func (_UserService) findUserByID(uid string) (*model.User, error) { - cachedUser, _ := cache.RetreiveEntity[model.User]("user", uid) - if cachedUser != nil { - return cachedUser, nil - } - ctx, cancel := global.TimeoutContext() - defer cancel() - user := &model.User{ - Id: uid, - } - err := global.DB.NewSelect().Model(&user).WherePK().Scan(ctx) - if err == nil { - cache.CacheEntity(*user, []string{fmt.Sprintf("user:%s", uid)}, "user", uid) - } - return user, err -} - -func (_UserService) ListUserDetail(keyword string, userType int, userState *bool, page int) ([]model.JoinedUserDetail, int64, error) { - var ( - cond = global.DB.NewSelect() - cacheConditions = make([]string, 0) - users = make([]model.User, 0) - ) - cond = cond.Model(&users).Relation("Detail") - cacheConditions = append(cacheConditions, strconv.Itoa(page)) - cond = cond.Where("detail.id <> ?", "000") - if len(keyword) != 0 { - keywordCond := "%" + keyword + "%" - cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery { - return q.Where("u.username like ?", keywordCond). - WhereOr("detail.name like ?", keywordCond) - }) - cacheConditions = append(cacheConditions, keyword) - } - if userType != -1 { - cond = cond.Where("u.type = ?", userType) - cacheConditions = append(cacheConditions, strconv.Itoa(userType)) - } - if userState != nil { - cond = cond.Where("u.enabled = ?", *userState) - cacheConditions = append(cacheConditions, strconv.FormatBool(*userState)) - } - startItem := (page - 1) * config.ServiceSettings.ItemsPageSize - - // * 这里利用已经构建完成的条件集合从缓存中获取数据,如果所有数据都可以从缓存中获取,那么就直接返回了。 - if cacheCounts, err := cache.RetreiveCount("join_user_detail", cacheConditions...); cacheCounts != -1 && err == nil { - if cachedUsers, _ := cache.RetreiveSearch[[]model.JoinedUserDetail]("join_user_detail", cacheConditions...); cachedUsers != nil { - return *cachedUsers, cacheCounts, nil - } - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - total, err := cond. - Limit(config.ServiceSettings.ItemsPageSize).Offset(startItem). - ScanAndCount(ctx) - - var ( - joinedUsers = make([]model.JoinedUserDetail, 0) - relations = []string{"user"} - ) - for _, u := range users { - joinedUsers = append(joinedUsers, model.JoinedUserDetail{ - UserDetail: *u.Detail, - Id: u.Id, - Username: u.Username, - Type: u.Type, - Enabled: u.Enabled, - }) - relations = append(relations, fmt.Sprintf("user:%s", u.Id)) - } - - cache.CacheCount(relations, "join_user_detail", int64(total), cacheConditions...) - cache.CacheSearch(joinedUsers, relations, "join_user_detail", cacheConditions...) - return joinedUsers, int64(total), err -} - -func (_UserService) FetchUserDetail(uid string) (*model.FullJoinedUserDetail, error) { - if cachedUser, _ := cache.RetreiveEntity[model.FullJoinedUserDetail]("full_join_user_detail", uid); cachedUser != nil { - return cachedUser, nil - } - - ctx, cancel := global.TimeoutContext() - defer cancel() - user := &model.User{} - err := global.DB.NewSelect().Model(user).Relation("Detail"). - Where("u.id = ?", uid). - Scan(ctx) - if err == nil { - fullJoinedUser := &model.FullJoinedUserDetail{ - User: *user, - UserDetail: *user.Detail, - } - cache.CacheEntity(*fullJoinedUser, []string{fmt.Sprintf("user:%s", uid)}, "full_join_user_detail", uid) - return fullJoinedUser, nil - } - return nil, err -} diff --git a/service/withdraw.go b/service/withdraw.go deleted file mode 100644 index dfb5066..0000000 --- a/service/withdraw.go +++ /dev/null @@ -1,160 +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" - "strconv" - "time" - - "github.com/samber/lo" - "github.com/uptrace/bun" - "go.uber.org/zap" -) - -type _WithdrawService struct { - l *zap.Logger -} - -var WithdrawService = _WithdrawService{ - l: logger.Named("Service", "Withdraw"), -} - -func (_WithdrawService) ApplyWithdraw(reportId string) (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 || 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) - if err != nil { - return false, exceptions.NewNotFoundError("未能找到匹配的系列报表。") - } - 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 -}