forked from free-lancers/electricity_bill_calc_service
		
	enhance(meter):基本完成表计大部分功能的放置。
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										180
									
								
								controller/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								controller/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | |||||||
|  | package controller | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"electricity_bill_calc/logger" | ||||||
|  | 	"electricity_bill_calc/repository" | ||||||
|  | 	"electricity_bill_calc/response" | ||||||
|  | 	"electricity_bill_calc/security" | ||||||
|  | 	"electricity_bill_calc/service" | ||||||
|  | 	"electricity_bill_calc/vo" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/gofiber/fiber/v2" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var meterLog = logger.Named("Handler", "Meter") | ||||||
|  |  | ||||||
|  | func InitializeMeterHandlers(router *fiber.App) { | ||||||
|  | 	router.Get("/meter/:pid", security.EnterpriseAuthorize, searchMetersWithinPark) | ||||||
|  | 	router.Post("/meter/:pid", security.EnterpriseAuthorize, createNewMeterManually) | ||||||
|  | 	router.Get("/meter/:pid/template", security.EnterpriseAuthorize, downloadMeterArchiveTemplate) | ||||||
|  | 	router.Get("/meter/:pid/:code", security.EnterpriseAuthorize, retrieveSpecificMeterDetail) | ||||||
|  | 	router.Put("/meter/:pid/:code", security.EnterpriseAuthorize, updateMeterManually) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 查询指定园区下的表计信息 | ||||||
|  | func searchMetersWithinPark(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("parkId") | ||||||
|  | 	meterLog.Info("查询指定园区下的表计信息", zap.String("park id", parkId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法查询指定园区下的表计信息,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	keyword := c.Query("keyword") | ||||||
|  | 	page := c.QueryInt("page", 1) | ||||||
|  | 	meters, total, err := repository.MeterRepository.MetersIn(parkId, uint(page), &keyword) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法查询指定园区下的表计信息,无法获取表计列表", zap.Error(err)) | ||||||
|  | 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return result.Success( | ||||||
|  | 		"已经取得符合条件的0.4kV表计列表。", | ||||||
|  | 		response.NewPagedResponse(page, total).ToMap(), | ||||||
|  | 		fiber.Map{"meters": meters}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 查询指定园区中指定表计的详细信息 | ||||||
|  | func retrieveSpecificMeterDetail(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("pid") | ||||||
|  | 	meterId := c.Params("code") | ||||||
|  | 	meterLog.Info("查询指定园区中指定表计的详细信息", zap.String("park id", parkId), zap.String("meter id", meterId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法查询指定园区中指定表计的详细信息,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	meter, err := repository.MeterRepository.FetchMeterDetail(parkId, meterId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法查询指定园区中指定表计的详细信息,无法获取表计信息", zap.Error(err)) | ||||||
|  | 		return result.Error(http.StatusInternalServerError, err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if meter == nil { | ||||||
|  | 		meterLog.Warn("无法查询指定园区中指定表计的详细信息,表计不存在") | ||||||
|  | 		return result.NotFound("指定的表计不存在。") | ||||||
|  | 	} | ||||||
|  | 	return result.Success("指定表计信息已经找到。", fiber.Map{"meter": meter}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 手动添加一条0.4kV表计记录 | ||||||
|  | func createNewMeterManually(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("pid") | ||||||
|  | 	meterLog.Info("手动添加一条0.4kV表计记录", zap.String("park id", parkId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法手动添加一条0.4kV表计记录,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var creationForm vo.MeterCreationForm | ||||||
|  | 	if err := c.BodyParser(&creationForm); err != nil { | ||||||
|  | 		meterLog.Error("无法手动添加一条0.4kV表计记录,无法解析表计创建表单", zap.Error(err)) | ||||||
|  | 		return result.NotAccept(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if err := service.MeterService.CreateMeterRecord(parkId, &creationForm); err != nil { | ||||||
|  | 		meterLog.Error("无法手动添加一条0.4kV表计记录,无法创建表计记录", zap.Error(err)) | ||||||
|  | 		return result.NotAccept(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return result.Created("新0.4kV表计已经添加完成。") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 手动更新一条新的0.4kV表计记录 | ||||||
|  | func updateMeterManually(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("pid") | ||||||
|  | 	meterId := c.Params("code") | ||||||
|  | 	meterLog.Info("手动更新一条新的0.4kV表计记录", zap.String("park id", parkId), zap.String("meter id", meterId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法手动更新一条新的0.4kV表计记录,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var updateForm vo.MeterModificationForm | ||||||
|  | 	if err := c.BodyParser(&updateForm); err != nil { | ||||||
|  | 		meterLog.Error("无法手动更新一条新的0.4kV表计记录,无法解析表计更新表单", zap.Error(err)) | ||||||
|  | 		return result.NotAccept(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if err := service.MeterService.UpdateMeterRecord(parkId, meterId, &updateForm); err != nil { | ||||||
|  | 		meterLog.Error("无法手动更新一条新的0.4kV表计记录,无法更新表计记录", zap.Error(err)) | ||||||
|  | 		return result.NotAccept(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return result.Updated("0.4kV表计已经更新完成。") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 下载指定的园区表计登记模板 | ||||||
|  | func downloadMeterArchiveTemplate(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("pid") | ||||||
|  | 	meterLog.Info("下载指定的园区表计登记模板", zap.String("park id", parkId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法下载指定的园区表计登记模板,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	parkDetail, err := repository.ParkRepository.RetrieveParkDetail(parkId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法下载指定的园区表计登记模板,无法获取园区信息", zap.Error(err)) | ||||||
|  | 		return result.NotFound(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return c.Download( | ||||||
|  | 		"./assets/meter_04kv_template.xlsx", | ||||||
|  | 		fmt.Sprintf("%s_表计档案.xlsx", parkDetail.Name), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 从Excel文件中导入表计档案 | ||||||
|  | func uploadMeterArchive(c *fiber.Ctx) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 更换系统中的表计 | ||||||
|  | func replaceMeter(c *fiber.Ctx) error { | ||||||
|  | 	parkId := c.Params("pid") | ||||||
|  | 	meterId := c.Params("code") | ||||||
|  | 	meterLog.Info("更换系统中的表计", zap.String("park id", parkId), zap.String("meter id", meterId)) | ||||||
|  | 	result := response.NewResult(c) | ||||||
|  | 	session, err := _retreiveSession(c) | ||||||
|  | 	if err != nil { | ||||||
|  | 		meterLog.Error("无法更换系统中的表计,无法获取当前用户会话", zap.Error(err)) | ||||||
|  | 		return result.Unauthorized(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if ok, err := checkParkBelongs(meterLog, parkId, session, &result); !ok { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var replacementForm vo.MeterReplacingForm | ||||||
|  | 	if err := c.BodyParser(&replacementForm); err != nil { | ||||||
|  | 		meterLog.Error("无法更换系统中的表计,无法解析表计更换表单", zap.Error(err)) | ||||||
|  | 		return result.NotAccept(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								model/cunsumption.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								model/cunsumption.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | import "github.com/shopspring/decimal" | ||||||
|  |  | ||||||
|  | type ConsumptionUnit struct { | ||||||
|  | 	Amount     decimal.Decimal `json:"amount"` | ||||||
|  | 	Fee        decimal.Decimal `json:"fee"` | ||||||
|  | 	Price      decimal.Decimal `json:"price"` | ||||||
|  | 	Proportion decimal.Decimal `json:"proportion"` | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								model/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								model/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MeterDetail struct { | ||||||
|  | 	Code           string              `json:"code" db:"code"` | ||||||
|  | 	Park           string              `json:"parkId" db:"park_id"` | ||||||
|  | 	Address        *string             `json:"address" db:"address"` | ||||||
|  | 	MeterType      int16               `json:"meterType" db:"meter_type"` | ||||||
|  | 	Building       *string             `json:"building" db:"building"` | ||||||
|  | 	BuildingName   *string             `json:"buildingName" db:"building_name"` | ||||||
|  | 	OnFloor        *string             `json:"onFloor" db:"on_floor"` | ||||||
|  | 	Area           decimal.NullDecimal `json:"area" db:"area"` | ||||||
|  | 	Ratio          decimal.Decimal     `json:"ratio" db:"ratio"` | ||||||
|  | 	Seq            int64               `json:"seq" db:"seq"` | ||||||
|  | 	Enabled        bool                `json:"enabled" db:"enabled"` | ||||||
|  | 	AttachedAt     *time.Time          `json:"attachedAt" db:"attached_at"` | ||||||
|  | 	DetachedAt     *time.Time          `json:"detachedAt" db:"detached_at"` | ||||||
|  | 	CreatedAt      time.Time           `json:"createdAt" db:"created_at"` | ||||||
|  | 	LastModifiedAt time.Time           `json:"lastModifiedAt" db:"last_modified_at"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MeterRelation struct { | ||||||
|  | 	Id            string     `json:"id"` | ||||||
|  | 	Park          string     `json:"parkId" db:"park_id"` | ||||||
|  | 	MasterMeter   string     `json:"masterMeterId" db:"master_meter_id"` | ||||||
|  | 	SlaveMeter    string     `json:"slaveMeterId" db:"slave_meter_id"` | ||||||
|  | 	EstablishedAt time.Time  `json:"establishedAt"` | ||||||
|  | 	SuspendedAt   *time.Time `json:"suspendedAt"` | ||||||
|  | 	RevokeAt      *time.Time `json:"revokeAt"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MeterSynchronization struct { | ||||||
|  | 	Park               string     `json:"parkId" db:"park_id"` | ||||||
|  | 	Meter              string     `json:"meterId" db:"meter_id"` | ||||||
|  | 	ForeignMeter       string     `json:"foreignMeter"` | ||||||
|  | 	SystemType         string     `json:"systemType"` | ||||||
|  | 	SystemIdentity     string     `json:"systemIdentity"` | ||||||
|  | 	Enabled            bool       `json:"enabled"` | ||||||
|  | 	LastSynchronizedAt time.Time  `json:"lastSynchronizedAt" db:"last_synchronized_at"` | ||||||
|  | 	RevokeAt           *time.Time `json:"revokeAt" db:"revoke_at"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SimpleMeterDocument struct { | ||||||
|  | 	Code         string          `json:"code"` | ||||||
|  | 	Seq          int64           `json:"seq"` | ||||||
|  | 	Address      *string         `json:"address"` | ||||||
|  | 	Ratio        decimal.Decimal `json:"ratio"` | ||||||
|  | 	TenementName *string         `json:"tenementName"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type NestedMeter struct { | ||||||
|  | 	MeterId             string          `json:"meterId"` | ||||||
|  | 	MeterDetail         MeterDetail     `json:"meterDetail"` | ||||||
|  | 	LastTermReadings    Reading         `json:"lastTermReadings"` | ||||||
|  | 	CurrentTermReadings Reading         `json:"currentTermReadings"` | ||||||
|  | 	Overall             ConsumptionUnit `json:"overall"` | ||||||
|  | 	Critical            ConsumptionUnit `json:"critical"` | ||||||
|  | 	Peak                ConsumptionUnit `json:"peak"` | ||||||
|  | 	Flat                ConsumptionUnit `json:"flat"` | ||||||
|  | 	Valley              ConsumptionUnit `json:"valley"` | ||||||
|  | 	BasicPooled         decimal.Decimal `json:"basicPooled"` | ||||||
|  | 	AdjustPooled        decimal.Decimal `json:"adjustPooled"` | ||||||
|  | 	LossPooled          decimal.Decimal `json:"lossPooled"` | ||||||
|  | 	PublicPooled        decimal.Decimal `json:"publicPooled"` | ||||||
|  | 	FinalTotal          decimal.Decimal `json:"finalTotal"` | ||||||
|  | 	Area                decimal.Decimal `json:"area"` | ||||||
|  | 	Proportion          decimal.Decimal `json:"proportion"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type PooledMeterDetailCompound struct { | ||||||
|  | 	MeterDetail | ||||||
|  | 	BindMeters []MeterDetail `json:"bindMeters"` | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								model/reading.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								model/reading.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Reading struct { | ||||||
|  | 	Ratio    decimal.Decimal `json:"ratio"` | ||||||
|  | 	Overall  decimal.Decimal `json:"overall"` | ||||||
|  | 	Critical decimal.Decimal `json:"critical"` | ||||||
|  | 	Peak     decimal.Decimal `json:"peak"` | ||||||
|  | 	Flat     decimal.Decimal `json:"flat"` | ||||||
|  | 	Valley   decimal.Decimal `json:"valley"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPVReading(ratio, overall, critical, peak, flat, valley decimal.Decimal) *Reading { | ||||||
|  | 	return &Reading{ | ||||||
|  | 		Ratio:    ratio, | ||||||
|  | 		Overall:  overall, | ||||||
|  | 		Critical: critical, | ||||||
|  | 		Peak:     peak, | ||||||
|  | 		Flat:     flat, | ||||||
|  | 		Valley:   valley, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewUnitaryReading(ratio, overall decimal.Decimal) *Reading { | ||||||
|  | 	return &Reading{ | ||||||
|  | 		Ratio:    ratio, | ||||||
|  | 		Overall:  overall, | ||||||
|  | 		Critical: decimal.Zero, | ||||||
|  | 		Peak:     decimal.Zero, | ||||||
|  | 		Flat:     overall, | ||||||
|  | 		Valley:   decimal.Zero, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MeterReading struct { | ||||||
|  | 	ReadAt    time.Time       `json:"readAt"` | ||||||
|  | 	Park      string          `json:"parkId" db:"park_id"` | ||||||
|  | 	Meter     string          `json:"meterId" db:"meter_id"` | ||||||
|  | 	MeterType int16           `json:"meterType"` | ||||||
|  | 	Ratio     decimal.Decimal `json:"ratio"` | ||||||
|  | 	Overall   decimal.Decimal `json:"overall"` | ||||||
|  | 	Critical  decimal.Decimal `json:"critical"` | ||||||
|  | 	Peak      decimal.Decimal `json:"peak"` | ||||||
|  | 	Flat      decimal.Decimal `json:"flat"` | ||||||
|  | 	Valley    decimal.Decimal `json:"valley"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type DetailedMeterReading struct { | ||||||
|  | 	Detail  MeterDetail  `json:"detail"` | ||||||
|  | 	Reading MeterReading `json:"reading"` | ||||||
|  | } | ||||||
							
								
								
									
										1091
									
								
								repository/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1091
									
								
								repository/meter.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -48,6 +48,7 @@ func App() *fiber.App { | |||||||
| 	controller.InitializeRegionHandlers(app) | 	controller.InitializeRegionHandlers(app) | ||||||
| 	controller.InitializeChargeHandlers(app) | 	controller.InitializeChargeHandlers(app) | ||||||
| 	controller.InitializeParkHandlers(app) | 	controller.InitializeParkHandlers(app) | ||||||
|  | 	controller.InitializeMeterHandlers(app) | ||||||
|  |  | ||||||
| 	return app | 	return app | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										539
									
								
								service/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										539
									
								
								service/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,539 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"electricity_bill_calc/cache" | ||||||
|  | 	"electricity_bill_calc/global" | ||||||
|  | 	"electricity_bill_calc/logger" | ||||||
|  | 	"electricity_bill_calc/model" | ||||||
|  | 	"electricity_bill_calc/repository" | ||||||
|  | 	"electricity_bill_calc/tools" | ||||||
|  | 	"electricity_bill_calc/types" | ||||||
|  | 	"electricity_bill_calc/vo" | ||||||
|  | 	"fmt" | ||||||
|  | 	"mime/multipart" | ||||||
|  |  | ||||||
|  | 	"github.com/samber/lo" | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type _MeterService struct { | ||||||
|  | 	log *zap.Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var MeterService = _MeterService{ | ||||||
|  | 	log: logger.Named("Service", "Meter"), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 创建一条新的表计记录 | ||||||
|  | func (ms _MeterService) CreateMeterRecord(pid string, form *vo.MeterCreationForm) error { | ||||||
|  | 	ms.log.Info("创建一条新的表计记录", zap.String("park id", pid)) | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ok, err := repository.MeterRepository.CreateMeter(tx, ctx, pid, *form) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法创建一条新的表计记录。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		ms.log.Error("数据库未能记录新的表计记录。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, form.Code, form.MeterType, form.Ratio, &form.MeterReadingForm) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法记录表计读数。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		ms.log.Error("数据库未能记录表计读数。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cache.AbolishRelation(fmt.Sprintf("meter:%s", pid)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 更新指定表计的信息 | ||||||
|  | func (ms _MeterService) UpdateMeterRecord(pid string, code string, form *vo.MeterModificationForm) error { | ||||||
|  | 	ms.log.Info("更新指定表计的信息", zap.String("park id", pid), zap.String("meter code", code)) | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ok, err := repository.MeterRepository.UpdateMeter(tx, ctx, pid, code, form) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法更新指定表计的信息。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		ms.log.Error("数据库未能更新指定表计的信息。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cache.AbolishRelation(fmt.Sprintf("meter:%s", pid)) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 处理上传的Excel格式表计档案文件,根据表号自动更新数据库 | ||||||
|  | func (ms _MeterService) BatchImportMeters(pid string, file multipart.FileHeader) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 更换系统中的表计 | ||||||
|  | func (ms _MeterService) ReplaceMeter( | ||||||
|  | 	pid string, | ||||||
|  | 	oldMeterCode string, | ||||||
|  | 	oldMeterReading *vo.MeterReadingForm, | ||||||
|  | 	newMeterCode string, | ||||||
|  | 	newMeterRatio decimal.Decimal, | ||||||
|  | 	newMeterReading *vo.MeterReadingForm, | ||||||
|  | ) error { | ||||||
|  | 	ms.log.Info("更换系统中的表计", zap.String("park id", pid), zap.String("old meter code", oldMeterCode), zap.String("new meter code", newMeterCode)) | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤1:读取旧表信息 | ||||||
|  | 	oldMeter, err := repository.MeterRepository.FetchMeterDetail(pid, oldMeterCode) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法读取旧表信息。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("要替换的旧表计不存在:%w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤2:写入旧表读数 | ||||||
|  | 	ok, err := repository.MeterRepository.RecordReading(tx, ctx, pid, oldMeterCode, oldMeter.MeterType, oldMeter.Ratio, oldMeterReading) | ||||||
|  | 	switch { | ||||||
|  | 	case err != nil: | ||||||
|  | 		ms.log.Error("无法写入旧表读数。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	case !ok: | ||||||
|  | 		ms.log.Error("数据库未能写入旧表读数。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("旧表计读数未能成功保存到数据库。") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤3:从系统移除旧表计 | ||||||
|  | 	ok, err = repository.MeterRepository.DetachMeter(tx, ctx, pid, oldMeterCode) | ||||||
|  | 	switch { | ||||||
|  | 	case err != nil: | ||||||
|  | 		ms.log.Error("无法从系统移除旧表计。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	case !ok: | ||||||
|  | 		ms.log.Error("未能从系统移除旧表计。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("旧表计未能成功从系统移除。") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤4:获取旧表计的关联信息 | ||||||
|  | 	var oldRelations []*model.MeterRelation | ||||||
|  | 	switch oldMeter.MeterType { | ||||||
|  | 	case model.METER_INSTALLATION_POOLING: | ||||||
|  | 		oldRelations, err = repository.MeterRepository.ListPooledMeterRelations(pid, oldMeterCode) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ms.log.Error("无法获取旧表计的关联信息。", zap.Error(err)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		oldRelations, err = repository.MeterRepository.ListMeterRelations(pid, oldMeterCode) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ms.log.Error("无法获取旧表计的关联信息。", zap.Error(err)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤5:将旧表计的关联信息设置为解除 | ||||||
|  | 	for _, relation := range oldRelations { | ||||||
|  | 		ok, err = repository.MeterRepository.UnbindMeter(tx, ctx, pid, relation.MasterMeter, relation.SlaveMeter) | ||||||
|  | 		switch { | ||||||
|  | 		case err != nil: | ||||||
|  | 			ms.log.Error("无法将旧表计的关联信息设置为解除。", zap.String("master meter", relation.MasterMeter), zap.String("slave meter", relation.SlaveMeter), zap.Error(err)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return err | ||||||
|  | 		case !ok: | ||||||
|  | 			ms.log.Error("未能将旧表计的关联信息设置为解除。", zap.String("master meter", relation.MasterMeter), zap.String("slave meter", relation.SlaveMeter)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return fmt.Errorf("旧表计的关联信息未能成功设置为解除。") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤6:将旧表计的部分信息赋予新表计 | ||||||
|  | 	newMeterCreationForm := vo.MeterCreationForm{ | ||||||
|  | 		Code:             newMeterCode, | ||||||
|  | 		Address:          oldMeter.Address, | ||||||
|  | 		MeterType:        oldMeter.MeterType, | ||||||
|  | 		Ratio:            newMeterRatio, | ||||||
|  | 		Seq:              oldMeter.Seq, | ||||||
|  | 		Enabled:          oldMeter.Enabled, | ||||||
|  | 		Building:         oldMeter.Building, | ||||||
|  | 		OnFloor:          oldMeter.OnFloor, | ||||||
|  | 		Area:             oldMeter.Area, | ||||||
|  | 		MeterReadingForm: *newMeterReading, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤7:将新表计写入系统 | ||||||
|  | 	ok, err = repository.MeterRepository.CreateMeter(tx, ctx, pid, newMeterCreationForm) | ||||||
|  | 	switch { | ||||||
|  | 	case err != nil: | ||||||
|  | 		ms.log.Error("无法将新表计写入系统。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	case !ok: | ||||||
|  | 		ms.log.Error("未能将新表计写入系统。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("新表计未能成功写入系统。") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤8:将新表计的读数写入系统 | ||||||
|  | 	ok, err = repository.MeterRepository.RecordReading(tx, ctx, pid, newMeterCode, newMeterCreationForm.MeterType, newMeterCreationForm.Ratio, &newMeterCreationForm.MeterReadingForm) | ||||||
|  | 	switch { | ||||||
|  | 	case err != nil: | ||||||
|  | 		ms.log.Error("无法将新表计的读数写入系统。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	case !ok: | ||||||
|  | 		ms.log.Error("未能将新表计的读数写入系统。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("新表计的读数未能成功写入系统。") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤9:将旧表计的关联信息复制一份赋予新表计 | ||||||
|  | 	switch oldMeter.MeterType { | ||||||
|  | 	case model.METER_INSTALLATION_POOLING: | ||||||
|  | 		for _, relation := range oldRelations { | ||||||
|  | 			ok, err = repository.MeterRepository.BindMeter(tx, ctx, pid, newMeterCode, relation.SlaveMeter) | ||||||
|  | 			switch { | ||||||
|  | 			case err != nil: | ||||||
|  | 				ms.log.Error("无法将旧表计的关联信息赋予新表计。", zap.String("master meter", newMeterCode), zap.String("slave meter", relation.SlaveMeter), zap.Error(err)) | ||||||
|  | 				tx.Rollback(ctx) | ||||||
|  | 				return err | ||||||
|  | 			case !ok: | ||||||
|  | 				ms.log.Error("未能将旧表计的关联信息赋予新表计。", zap.String("master meter", newMeterCode), zap.String("slave meter", relation.SlaveMeter)) | ||||||
|  | 				tx.Rollback(ctx) | ||||||
|  | 				return fmt.Errorf("旧表计的关联信息未能成功赋予新表计。") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		for _, relation := range oldRelations { | ||||||
|  | 			ok, err = repository.MeterRepository.BindMeter(tx, ctx, pid, relation.MasterMeter, newMeterCode) | ||||||
|  | 			switch { | ||||||
|  | 			case err != nil: | ||||||
|  | 				ms.log.Error("无法将旧表计的关联信息赋予新表计。", zap.String("master meter", relation.MasterMeter), zap.String("slave meter", newMeterCode), zap.Error(err)) | ||||||
|  | 				tx.Rollback(ctx) | ||||||
|  | 				return err | ||||||
|  | 			case !ok: | ||||||
|  | 				ms.log.Error("未能将旧表计的关联信息赋予新表计。", zap.String("master meter", relation.MasterMeter), zap.String("slave meter", newMeterCode)) | ||||||
|  | 				tx.Rollback(ctx) | ||||||
|  | 				return fmt.Errorf("旧表计的关联信息未能成功赋予新表计。") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 步骤10:提交事务 | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	cache.AbolishRelation(fmt.Sprintf("meter:%s", pid)) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 列出园区中指定公摊表计下的所有关联表计 | ||||||
|  | func (ms _MeterService) ListPooledMeterRelations(pid, masterMeter string) ([]*model.MeterDetail, error) { | ||||||
|  | 	ms.log.Info("列出园区中指定公摊表计下的所有关联表计", zap.String("park id", pid), zap.String("meter code", masterMeter)) | ||||||
|  | 	relations, err := repository.MeterRepository.ListPooledMeterRelations(pid, masterMeter) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法列出园区中指定公摊表计下的所有关联关系。", zap.Error(err)) | ||||||
|  | 		return make([]*model.MeterDetail, 0), err | ||||||
|  | 	} | ||||||
|  | 	relatedMeterCodes := lo.Map(relations, func(element *model.MeterRelation, _ int) string { | ||||||
|  | 		return element.SlaveMeter | ||||||
|  | 	}) | ||||||
|  | 	meters, err := repository.MeterRepository.ListMetersByIDs(pid, relatedMeterCodes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法列出园区中指定公摊表计下的所有关联表计详细信息。", zap.Error(err)) | ||||||
|  | 		return make([]*model.MeterDetail, 0), err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return meters, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 列出指定园区中所有的公摊表计 | ||||||
|  | func (ms _MeterService) SearchPooledMetersDetail(pid string, page uint, keyword *string) ([]*model.PooledMeterDetailCompound, int64, error) { | ||||||
|  | 	ms.log.Info("列出指定园区中所有的公摊表计", zap.String("park id", pid), zap.Uint("page", page), zap.String("keyword", *keyword)) | ||||||
|  | 	cacheConditions := []string{ | ||||||
|  | 		pid, | ||||||
|  | 		fmt.Sprintf("%d", page), | ||||||
|  | 		tools.DefaultTo(keyword, "UNDEFINED"), | ||||||
|  | 	} | ||||||
|  | 	if meters, total, err := cache.RetrievePagedSearch[[]*model.PooledMeterDetailCompound]("assemble_pooled_meters_detail", cacheConditions...); err == nil { | ||||||
|  | 		ms.log.Info("已经从缓存中获取到了指定园区中所有的公摊表计。", zap.Int("count", len(*meters)), zap.Int64("total", total)) | ||||||
|  | 		return *meters, total, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	poolingMeters, total, err := repository.MeterRepository.ListPoolingMeters(pid, page, keyword) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法列出指定园区中所有的公摊表计。", zap.Error(err)) | ||||||
|  | 		return make([]*model.PooledMeterDetailCompound, 0), 0, err | ||||||
|  | 	} | ||||||
|  | 	poolingMeterIds := lo.Map(poolingMeters, func(element *model.MeterDetail, _ int) string { | ||||||
|  | 		return element.Code | ||||||
|  | 	}) | ||||||
|  | 	relations, err := repository.MeterRepository.ListPooledMeterRelationsByCodes(pid, poolingMeterIds) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法列出指定园区中所有的公摊表计关联关系。", zap.Error(err)) | ||||||
|  | 		return make([]*model.PooledMeterDetailCompound, 0), 0, err | ||||||
|  | 	} | ||||||
|  | 	slaveMeters, err := repository.MeterRepository.ListMetersByIDs(pid, lo.Map(relations, func(element *model.MeterRelation, _ int) string { | ||||||
|  | 		return element.SlaveMeter | ||||||
|  | 	})) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法列出指定园区中所有的公摊表计的关联表计详细信息。", zap.Error(err)) | ||||||
|  | 		return make([]*model.PooledMeterDetailCompound, 0), 0, err | ||||||
|  | 	} | ||||||
|  | 	var assembled []*model.PooledMeterDetailCompound = make([]*model.PooledMeterDetailCompound, 0) | ||||||
|  | 	for _, meter := range poolingMeters { | ||||||
|  | 		slaveIDs := lo.Map(lo.Filter( | ||||||
|  | 			relations, | ||||||
|  | 			func(element *model.MeterRelation, _ int) bool { | ||||||
|  | 				return element.MasterMeter == meter.Code | ||||||
|  | 			}), | ||||||
|  | 			func(element *model.MeterRelation, _ int) string { | ||||||
|  | 				return element.SlaveMeter | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 		slaves := lo.Map(lo.Filter( | ||||||
|  | 			slaveMeters, | ||||||
|  | 			func(element *model.MeterDetail, _ int) bool { | ||||||
|  | 				return lo.Contains(slaveIDs, element.Code) | ||||||
|  | 			}), | ||||||
|  | 			func(element *model.MeterDetail, _ int) model.MeterDetail { | ||||||
|  | 				return *element | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 		assembled = append(assembled, &model.PooledMeterDetailCompound{ | ||||||
|  | 			MeterDetail: *meter, | ||||||
|  | 			BindMeters:  slaves, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cache.CachePagedSearch(assembled, total, []string{fmt.Sprintf("meter:%s", pid), fmt.Sprintf("meter_relation:%s", pid)}, "assemble_pooled_meter_detail", cacheConditions...) | ||||||
|  |  | ||||||
|  | 	return assembled, total, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 批量向园区中指定公摊表计下绑定关联表计 | ||||||
|  | func (ms _MeterService) BindMeter(pid, masterMeter string, slaveMeters []string) (bool, error) { | ||||||
|  | 	ms.log.Info("批量向园区中指定公摊表计下绑定关联表计", zap.String("park id", pid), zap.String("master meter", masterMeter), zap.Strings("slave meters", slaveMeters)) | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, slave := range slaveMeters { | ||||||
|  | 		ok, err := repository.MeterRepository.BindMeter(tx, ctx, pid, masterMeter, slave) | ||||||
|  | 		switch { | ||||||
|  | 		case err != nil: | ||||||
|  | 			ms.log.Error("无法向园区中指定公摊表计下绑定关联表计。", zap.String("master meter", masterMeter), zap.String("slave meter", slave), zap.Error(err)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return false, err | ||||||
|  | 		case !ok: | ||||||
|  | 			ms.log.Error("未能向园区中指定公摊表计下绑定关联表计。", zap.String("master meter", masterMeter), zap.String("slave meter", slave)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return false, fmt.Errorf("未能成功向园区中指定公摊表计下绑定关联表计。") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	cache.AbolishRelation(fmt.Sprintf("meter:%s", pid)) | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 批量解绑园区中指定表计下的指定表计 | ||||||
|  | func (ms _MeterService) UnbindMeter(pid, masterMeter string, slaveMeters []string) (bool, error) { | ||||||
|  | 	ms.log.Info("批量解绑园区中指定表计下的指定表计", zap.String("park id", pid), zap.String("master meter", masterMeter), zap.Strings("slave meters", slaveMeters)) | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, slave := range slaveMeters { | ||||||
|  | 		ok, err := repository.MeterRepository.UnbindMeter(tx, ctx, pid, masterMeter, slave) | ||||||
|  | 		switch { | ||||||
|  | 		case err != nil: | ||||||
|  | 			ms.log.Error("无法解绑园区中指定表计下的指定表计。", zap.String("master meter", masterMeter), zap.String("slave meter", slave), zap.Error(err)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return false, err | ||||||
|  | 		case !ok: | ||||||
|  | 			ms.log.Error("未能解绑园区中指定表计下的指定表计。", zap.String("master meter", masterMeter), zap.String("slave meter", slave)) | ||||||
|  | 			tx.Rollback(ctx) | ||||||
|  | 			return false, fmt.Errorf("未能成功解绑园区中指定表计下的指定表计。") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	cache.AbolishRelation(fmt.Sprintf("meter:%s", pid)) | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 查询符合条件的表计读数记录 | ||||||
|  | func (ms _MeterService) SearchMeterReadings(pid string, building *string, start, end *types.Date, page uint, keyword *string) ([]*model.DetailedMeterReading, int64, error) { | ||||||
|  | 	ms.log.Info( | ||||||
|  | 		"查询符合条件的表计读数记录", | ||||||
|  | 		zap.String("park id", pid), | ||||||
|  | 		zap.Stringp("building", building), | ||||||
|  | 		logger.DateFieldp("start", start), | ||||||
|  | 		logger.DateFieldp("end", end), | ||||||
|  | 		zap.Uint("page", page), | ||||||
|  | 		zap.Stringp("keyword", keyword), | ||||||
|  | 	) | ||||||
|  | 	readings, total, err := repository.MeterRepository.ListMeterReadings(pid, keyword, page, start, end, building) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法查询符合条件的表计读数记录。", zap.Error(err)) | ||||||
|  | 		return make([]*model.DetailedMeterReading, 0), 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	meterCodes := lo.Map(readings, func(element *model.MeterReading, _ int) string { | ||||||
|  | 		return element.Meter | ||||||
|  | 	}) | ||||||
|  | 	meterDetails, err := repository.MeterRepository.ListMetersByIDs(pid, meterCodes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法查询符合条件的表计读数记录的表计详细信息。", zap.Error(err)) | ||||||
|  | 		return make([]*model.DetailedMeterReading, 0), 0, err | ||||||
|  | 	} | ||||||
|  | 	assembles := lo.Map( | ||||||
|  | 		readings, | ||||||
|  | 		func(element *model.MeterReading, _ int) *model.DetailedMeterReading { | ||||||
|  | 			meter, _ := lo.Find(meterDetails, func(detail *model.MeterDetail) bool { | ||||||
|  | 				return detail.Code == element.Meter | ||||||
|  | 			}) | ||||||
|  | 			return &model.DetailedMeterReading{ | ||||||
|  | 				Detail:  *meter, | ||||||
|  | 				Reading: *element, | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	return assembles, total, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 创建一条新的表计抄表记录 | ||||||
|  | func (ms _MeterService) RecordReading(pid, meterCode string, form *vo.MeterReadingForm) error { | ||||||
|  | 	ms.log.Info("创建一条新的表计抄表记录", zap.String("park id", pid), zap.String("meter code", meterCode)) | ||||||
|  | 	meter, err := repository.MeterRepository.FetchMeterDetail(pid, meterCode) | ||||||
|  | 	if err != nil || meter == nil { | ||||||
|  | 		ms.log.Error("无法找到指定的表计", zap.Error(err)) | ||||||
|  | 		return fmt.Errorf("无法找到指定的表计:%w", err) | ||||||
|  | 	} | ||||||
|  | 	ctx, cancel := global.TimeoutContext() | ||||||
|  | 	defer cancel() | ||||||
|  |  | ||||||
|  | 	tx, err := global.DB.Begin(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法启动数据库事务。", zap.Error(err)) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	ok, err := repository.MeterRepository.RecordReading(tx, ctx, pid, meterCode, meter.MeterType, meter.Ratio, form) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("无法创建一条新的表计抄表记录。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		ms.log.Error("未能创建一条新的表计抄表记录。") | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return fmt.Errorf("未能成功创建一条新的表计抄表记录。") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = tx.Commit(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ms.log.Error("未能成功提交数据库事务。", zap.Error(err)) | ||||||
|  | 		tx.Rollback(ctx) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 获取指定园区的全部待抄表计列表,并将其输出到Excel文件模板中,提供生成文件的二进制内容 | ||||||
|  | func (ms _MeterService) GenerateParkMeterReadingTemplate(pid string, meters []*model.SimpleMeterDocument) ([]byte, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 处理上传的Excel格式的表计抄表记录,所有满足审查条件的记录都将被保存到数据库中。 | ||||||
|  | // 无论峰谷表计还是普通表计,只要抄表记录中不存在峰谷数据,都将自动使用平段配平。 | ||||||
|  | func (ms _MeterService) BatchImportReadings(pid string, uploadContent []byte) error { | ||||||
|  | 	// 步骤1:将解析到的数据转换成创建表单数据 | ||||||
|  | 	// 步骤2:对目前已经解析到的数据进行合法性检测,检测包括表计编号在同一抄表时间是否重复 | ||||||
|  | 	// 步骤3:从数据库中获取当前园区中已有的表计编号 | ||||||
|  | 	// 步骤4.0:启动数据库事务 | ||||||
|  | 	// 步骤4.1:对比检查数据库中的表计编号与上传文件中的表计编号是否存在差异。非差异内容将直接保存 | ||||||
|  | 	// 步骤4.1.1:抄表的表计在数据库中已经存在,可以直接保存起数据。 | ||||||
|  | 	// 步骤4.1.2:抄表表计在数据库中不存在,需要将其记录进入错误。 | ||||||
|  | 	// 步骤4.3:如果批处理过程中存在错误,撤销全部导入动作。 | ||||||
|  | 	// 步骤5:执行事务,更新数据库,获取完成更改的行数。 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								vo/meter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vo/meter.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | package vo | ||||||
|  |  | ||||||
|  | import "github.com/shopspring/decimal" | ||||||
|  |  | ||||||
|  | type MeterCreationForm struct { | ||||||
|  | 	Code             string              `json:"code"` | ||||||
|  | 	Address          *string             `json:"address"` | ||||||
|  | 	Ratio            decimal.Decimal     `json:"ratio"` | ||||||
|  | 	Seq              int64               `json:"seq"` | ||||||
|  | 	MeterType        int16               `json:"meterType"` | ||||||
|  | 	Building         *string             `json:"building"` | ||||||
|  | 	OnFloor          *string             `json:"onFloor"` | ||||||
|  | 	Area             decimal.NullDecimal `json:"area"` | ||||||
|  | 	Enabled          bool                `json:"enabled"` | ||||||
|  | 	MeterReadingForm `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MeterModificationForm struct { | ||||||
|  | 	Address   *string             `json:"address"` | ||||||
|  | 	Seq       int64               `json:"seq"` | ||||||
|  | 	Ratio     decimal.Decimal     `json:"ratio"` | ||||||
|  | 	Enabled   bool                `json:"enabled"` | ||||||
|  | 	MeterType int16               `json:"meterType"` | ||||||
|  | 	Building  *string             `json:"building"` | ||||||
|  | 	OnFloor   *string             `json:"onFloor"` | ||||||
|  | 	Area      decimal.NullDecimal `json:"area"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type NewMeterForReplacingForm struct { | ||||||
|  | 	Code    string           `json:"code"` | ||||||
|  | 	Ratio   decimal.Decimal  `json:"ratio"` | ||||||
|  | 	Reading MeterReadingForm `json:"reading"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MeterReplacingForm struct { | ||||||
|  | 	OldReading MeterReadingForm         `json:"oldReading"` | ||||||
|  | 	NewMeter   NewMeterForReplacingForm `json:"newMeter"` | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								vo/reading.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vo/reading.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | package vo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"electricity_bill_calc/types" | ||||||
|  |  | ||||||
|  | 	"github.com/shopspring/decimal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MeterReadingForm struct { | ||||||
|  | 	Overall  decimal.Decimal `json:"overall"` | ||||||
|  | 	Critical decimal.Decimal `json:"critical"` | ||||||
|  | 	Peak     decimal.Decimal `json:"peak"` | ||||||
|  | 	Flat     decimal.Decimal `json:"flat"` | ||||||
|  | 	Valley   decimal.Decimal `json:"valley"` | ||||||
|  | 	ReadAt   *types.DateTime `json:"readAt"` | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user