From fbe4036389d8a9811f1274258da5a7c904e8959d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Tue, 20 Jun 2023 16:50:34 +0800 Subject: [PATCH] =?UTF-8?q?enhance(report):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=8A=A5=E8=A1=A8=E9=83=A8=E5=88=86=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E6=9F=A5=E8=AF=A2=E6=93=8D=E4=BD=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repository/report.go | 827 +++++++++++++++++++++++++++++++++++++++++++ vo/report.go | 41 +++ 2 files changed, 868 insertions(+) diff --git a/repository/report.go b/repository/report.go index 4e251d6..9cf928f 100644 --- a/repository/report.go +++ b/repository/report.go @@ -1,10 +1,20 @@ package repository import ( + "electricity_bill_calc/config" + "electricity_bill_calc/exceptions" + "electricity_bill_calc/global" "electricity_bill_calc/logger" + "electricity_bill_calc/model" + "electricity_bill_calc/tools/serial" + "electricity_bill_calc/types" + "electricity_bill_calc/vo" + "fmt" "github.com/doug-martin/goqu/v9" _ "github.com/doug-martin/goqu/v9/dialect/postgres" + "github.com/georgysavva/scany/v2/pgxscan" + "github.com/samber/lo" "go.uber.org/zap" ) @@ -17,3 +27,820 @@ var ReportRepository = _ReportRepository{ log: logger.Named("Repository", "Report"), ds: goqu.Dialect("postgres"), } + +// 检查指定核算报表的归属情况 +func (rr _ReportRepository) IsBelongsTo(rid, uid string) (bool, error) { + rr.log.Info("检查指定核算报表的归属", zap.String("User", uid), zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Select(goqu.COUNT("r.*")). + Where( + goqu.I("r.id").Eq(rid), + goqu.I("p.user_id").Eq(uid), + ). + Prepared(true).ToSQL() + + var count int64 + if err := pgxscan.Get(ctx, global.DB, &count, querySql, queryParams...); err != nil { + rr.log.Error("检查指定核算报表的归属出现错误", zap.Error(err)) + return false, err + } + return count > 0, nil +} + +// 获取指定用户下所有园区的尚未发布的简易核算报表索引内容 +func (rr _ReportRepository) ListDraftReportIndicies(uid string) ([]*model.ReportIndex, error) { + rr.log.Info("获取指定用户下的所有尚未发布的报表索引", zap.String("User", uid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Select("r.*", goqu.I("t.status"), goqu.I("t.message")). + Where( + goqu.I("p.user_id").Eq(uid), + goqu.I("r.published").IsFalse(), + ). + Order(goqu.I("r.created_at").Desc()). + Prepared(true).ToSQL() + + var indicies []*model.ReportIndex = make([]*model.ReportIndex, 0) + if err := pgxscan.Select(ctx, global.DB, &indicies, querySql, queryParams...); err != nil { + rr.log.Error("获取指定用户下的所有尚未发布的报表索引出现错误", zap.Error(err)) + return indicies, err + } + return indicies, nil +} + +// 获取指定报表的详细索引内容 +func (rr _ReportRepository) GetReportIndex(rid string) (*model.ReportIndex, error) { + rr.log.Info("获取指定报表的详细索引", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Select("r.*", goqu.I("t.status"), goqu.I("t.message")). + Where(goqu.I("r.id").Eq(rid)). + Prepared(true).ToSQL() + + var index model.ReportIndex + if err := pgxscan.Get(ctx, global.DB, &index, querySql, queryParams...); err != nil { + rr.log.Error("获取指定报表的详细索引出现错误", zap.Error(err)) + return nil, err + } + return &index, nil +} + +// 为指园区创建一个新的核算报表 +func (rr _ReportRepository) CreateReport(form *vo.ReportCreationForm) (bool, error) { + rr.log.Info("为指定园区创建一个新的核算报表", zap.String("Park", form.Park)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + tx, err := global.DB.Begin(ctx) + if err != nil { + rr.log.Error("未能开始一个数据库事务", zap.Error(err)) + return false, err + } + park, err := ParkRepository.RetrieveParkDetail(form.Park) + if err != nil || park == nil { + rr.log.Error("未能获取指定园区的详细信息", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewNotFoundErrorFromError("未能获取指定园区的详细信息", err) + } + createTime := types.Now() + periodRange := types.NewDateRange(&form.PeriodBegin, &form.PeriodEnd) + serial.StringSerialRequestChan <- 1 + reportId := serial.Prefix("R", <-serial.StringSerialResponseChan) + + createSql, createArgs, _ := rr.ds. + Insert(goqu.T("report")). + Cols( + "id", "park_id", "period", "category", "meter_o4kv_type", "price_policy", + "basic_pooled", "adjust_pooled", "loss_pooled", "public_pooled", "created_at", + "last_modified_at", + ). + Vals(goqu.Vals{ + reportId, park.Id, periodRange, park.Category, park.MeterType, park.PricePolicy, + park.BasicPooled, park.AdjustPooled, park.LossPooled, park.PublicPooled, createTime, + createTime, + }). + Prepared(true).ToSQL() + summarySql, summaryArgs, _ := rr.ds. + Insert(goqu.T("report_summary")). + Cols( + "report_id", "overall", "critical", "peak", "flat", "valley", "basic_fee", + "adjust_fee", + ). + Vals(goqu.Vals{ + reportId, + model.ConsumptionUnit{ + Amount: form.Overall, + Fee: form.OverallFee, + }, + model.ConsumptionUnit{ + Amount: form.Critical, + Fee: form.CriticalFee, + }, + model.ConsumptionUnit{ + Amount: form.Peak, + Fee: form.PeakFee, + }, + model.ConsumptionUnit{ + Amount: form.Flat, + Fee: form.FlatFee, + }, + model.ConsumptionUnit{ + Amount: form.Valley, + Fee: form.ValleyFee, + }, + form.BasicFee, + form.AdjustFee, + }). + Prepared(true).ToSQL() + taskSql, taskArgs, _ := rr.ds. + Insert(goqu.T("report_task")). + Cols("id", "status", "last_modified_at"). + Vals(goqu.Vals{reportId, model.REPORT_CALCULATE_TASK_STATUS_PENDING, createTime}). + Prepared(true).ToSQL() + + resIndex, err := tx.Exec(ctx, createSql, createArgs...) + if err != nil { + rr.log.Error("创建核算报表索引时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + if resIndex.RowsAffected() == 0 { + rr.log.Error("保存核算报表索引时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewUnsuccessCreateError("创建核算报表索引时出现错误") + } + resSummary, err := tx.Exec(ctx, summarySql, summaryArgs...) + if err != nil { + rr.log.Error("创建核算报表汇总时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + if resSummary.RowsAffected() == 0 { + rr.log.Error("保存核算报表汇总时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewUnsuccessCreateError("创建核算报表汇总时出现错误") + } + resTask, err := tx.Exec(ctx, taskSql, taskArgs...) + if err != nil { + rr.log.Error("创建核算报表任务时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + if resTask.RowsAffected() == 0 { + rr.log.Error("保存核算报表任务时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewUnsuccessCreateError("创建核算报表任务时出现错误") + } + err = tx.Commit(ctx) + if err != nil { + rr.log.Error("提交核算报表创建事务时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0 && resTask.RowsAffected() > 0, nil +} + +// 更新报表的基本信息 +func (rr _ReportRepository) UpdateReportSummary(rid string, form *vo.ReportModifyForm) (bool, error) { + rr.log.Info("更新指定报表的基本信息", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + tx, err := global.DB.Begin(ctx) + if err != nil { + rr.log.Error("未能开始一个数据库事务", zap.Error(err)) + return false, err + } + + updateTime := types.Now() + newPeriod := types.NewDateRange(&form.PeriodBegin, &form.PeriodEnd) + + udpateIndexSql, updateIndexArgs, _ := rr.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "period": newPeriod, + "last_modified_at": updateTime, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + updateSummarySql, updateSummaryArgs, _ := rr.ds. + Update(goqu.T("report_summary")). + Set(goqu.Record{ + "overall": model.ConsumptionUnit{Amount: form.Overall, Fee: form.OverallFee}, + "critical": model.ConsumptionUnit{Amount: form.Critical, Fee: form.CriticalFee}, + "peak": model.ConsumptionUnit{Amount: form.Peak, Fee: form.PeakFee}, + "flat": model.ConsumptionUnit{Amount: form.Flat, Fee: form.FlatFee}, + "valley": model.ConsumptionUnit{Amount: form.Valley, Fee: form.ValleyFee}, + "basic_fee": form.BasicFee, + "adjust_fee": form.AdjustFee, + }). + Where(goqu.I("report_id").Eq(rid)). + Prepared(true).ToSQL() + + resIndex, err := tx.Exec(ctx, udpateIndexSql, updateIndexArgs...) + if err != nil { + rr.log.Error("更新核算报表索引时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + if resIndex.RowsAffected() == 0 { + rr.log.Error("保存核算报表索引时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewUnsuccessUpdateError("更新核算报表索引时出现错误") + } + resSummary, err := tx.Exec(ctx, updateSummarySql, updateSummaryArgs...) + if err != nil { + rr.log.Error("更新核算报表汇总时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + if resSummary.RowsAffected() == 0 { + rr.log.Error("保存核算报表汇总时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, exceptions.NewUnsuccessUpdateError("更新核算报表汇总时出现错误") + } + + err = tx.Commit(ctx) + if err != nil { + rr.log.Error("提交核算报表更新事务时出现错误", zap.Error(err)) + tx.Rollback(ctx) + return false, err + } + return resIndex.RowsAffected() > 0 && resSummary.RowsAffected() > 0, nil +} + +// 获取指定报表的总览信息 +func (rr _ReportRepository) RetrieveReportSummary(rid string) (*model.ReportSummary, error) { + rr.log.Info("获取指定报表的总览信息", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report_summary")). + Select("*"). + Where(goqu.I("report_id").Eq(rid)). + Prepared(true).ToSQL() + + var summary model.ReportSummary + if err := pgxscan.Get(ctx, global.DB, &summary, querySql, queryParams...); err != nil { + rr.log.Error("获取指定报表的总览信息时出现错误", zap.Error(err)) + return nil, err + } + return &summary, nil +} + +// 获取指定用户的尚未发布的核算报表的计算状态 +func (rr _ReportRepository) GetReportTaskStatus(uid string) ([]*model.ReportTask, error) { + rr.log.Info("获取指定用户的尚未发布的核算报表的计算状态", zap.String("User", uid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report_task").As("t")). + Join(goqu.T("report").As("r"), goqu.On(goqu.I("r.id").Eq(goqu.I("t.id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Select( + goqu.I("t.*"), + ). + Where( + goqu.I("p.user_id").Eq(uid), + goqu.I("r.published").IsFalse(), + ). + Prepared(true).ToSQL() + + var tasks []*model.ReportTask = make([]*model.ReportTask, 0) + if err := pgxscan.Select(ctx, global.DB, &tasks, querySql, queryParams...); err != nil { + rr.log.Error("获取指定用户的尚未发布的核算报表的计算状态时出现错误", zap.Error(err)) + return tasks, err + } + return tasks, nil +} + +// 检索指定核算报表中园区公共表计的核算记录 +func (rr _ReportRepository) ListPublicMetersInReport(rid string, page uint, keyword *string) ([]*model.ReportDetailedPublicConsumption, int64, error) { + rr.log.Info("检索指定核算报表中园区公共表计的核算记录", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + reportQuery := rr.ds. + From(goqu.T("report_public_consumption").As("r")). + Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))). + LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))). + Select( + goqu.I("r.*"), goqu.I("b.name").As("building_name"), goqu.I("p.public_pooled"), + ). + Where(goqu.I("r.report_id").Eq(rid)) + countQuery := rr.ds. + From(goqu.T("report_public_consumption").As("r")). + Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))). + LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))). + Select(goqu.COUNT(goqu.I("r.*"))). + Where(goqu.I("r.report_id").Eq(rid)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery. + Order(goqu.I("m.code").Asc()). + Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + var ( + consumptions []*model.ReportDetailedPublicConsumption = make([]*model.ReportDetailedPublicConsumption, 0) + count int64 + ) + querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + if err := pgxscan.Select(ctx, global.DB, &consumptions, querySql, queryArgs...); err != nil { + rr.log.Error("检索指定核算报表中园区公共表计的核算记录时出现错误", zap.Error(err)) + return consumptions, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil { + rr.log.Error("检索指定核算报表中园区公共表计的核算记录时出现错误", zap.Error(err)) + return consumptions, 0, err + } + return consumptions, count, nil +} + +// 检索指定核算报表中公摊表计的核算记录 +func (rr _ReportRepository) ListPooledMetersInReport(rid string, page uint, keyword *string) ([]*model.ReportDetailedPooledConsumption, int64, error) { + rr.log.Info("检索指定核算报表中公摊表计的核算记录", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + reportQuery := rr.ds. + From(goqu.T("report_pooled_consumption").As("r")). + Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))). + LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))). + Select( + goqu.I("r.*"), goqu.I("b.name").As("building_name"), goqu.I("p.public_pooled"), + ). + Where(goqu.I("r.report_id").Eq(rid)) + countQuery := rr.ds. + From(goqu.T("report_pooled_consumption").As("r")). + Join(goqu.T("meter_04kv").As("m"), goqu.On(goqu.I("m.code").Eq(goqu.I("r.park_meter_id")))). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("m.park_id")))). + LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))). + Select(goqu.COUNT(goqu.I("r.*"))). + Where(goqu.I("r.report_id").Eq(rid)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("m.code").ILike(pattern), + goqu.I("m.address").ILike(pattern), + )) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery. + Order(goqu.I("m.code").Asc()). + Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + var ( + consumptions []*model.ReportDetailedPooledConsumption = make([]*model.ReportDetailedPooledConsumption, 0) + count int64 + ) + querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + if err := pgxscan.Select(ctx, global.DB, &consumptions, querySql, queryArgs...); err != nil { + rr.log.Error("检索指定核算报表中公摊表计的核算记录时出现错误", zap.Error(err)) + return consumptions, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil { + rr.log.Error("检索指定核算报表中公摊表计的核算记录时出现错误", zap.Error(err)) + return consumptions, 0, err + } + return consumptions, count, nil +} + +// 列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计及分摊详细 +func (rr _ReportRepository) ListPooledMeterDetailInReport(rid, mid string) ([]*model.ReportDetailNestedMeterConsumption, error) { + rr.log.Info("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计及分摊详细", zap.String("Report", rid), zap.String("Meter", mid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + meterSql, meterArgs, _ := rr.ds. + From(goqu.T("report_pooled_consumption")). + Select("*"). + Where(goqu.I("report_id").Eq(rid), goqu.I("pooled_meter_id").Eq(mid)). + Prepared(true).ToSQL() + + var meter model.ReportPooledConsumption + if err := pgxscan.Get(ctx, global.DB, &meter, meterSql, meterArgs...); err != nil { + rr.log.Error("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计编号时出现错误", zap.Error(err)) + return make([]*model.ReportDetailNestedMeterConsumption, 0), err + } + meterCodes := lo.Map(meter.Diluted, func(m model.NestedMeter, _ int) string { + return m.MeterId + }) + + meterSql, meterArgs, _ = rr.ds. + From(goqu.T("meter_04kv").As("m")). + LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("b.id").Eq(goqu.I("m.building")))). + Select( + goqu.I("m.*"), goqu.I("b.name").As("building_name"), + ). + Where(goqu.I("m.code").In(meterCodes)). + Prepared(true).ToSQL() + var meterDetails []*model.MeterDetail = make([]*model.MeterDetail, 0) + if err := pgxscan.Select(ctx, global.DB, &meterDetails, meterSql, meterArgs...); err != nil { + rr.log.Error("列出指定核算报表中指定公共表计下参与公共表计费用分摊的表计详细时出现错误", zap.Error(err)) + return make([]*model.ReportDetailNestedMeterConsumption, 0), err + } + assembled := lo.Map(meter.Diluted, func(m model.NestedMeter, _ int) *model.ReportDetailNestedMeterConsumption { + meterDetail, _ := lo.Find(meterDetails, func(elem *model.MeterDetail) bool { + return elem.Code == m.MeterId + }) + return &model.ReportDetailNestedMeterConsumption{ + Meter: *meterDetail, + Consumption: m, + } + }) + return assembled, nil +} + +// 列出指定核算报表下商户的简要计费信息 +func (rr _ReportRepository) ListTenementInReport(rid string, page uint, keyword *string) ([]*model.ReportTenement, int64, error) { + rr.log.Info("查询指定核算报表下的商户简要计费信息", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + reportQuery := rr.ds. + From(goqu.T("report_tenement").As("rt")). + Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("rt.tenement_id")))). + Select("rt.*"). + Where(goqu.I("rt.report_id").Eq(rid)) + countQuery := rr.ds. + From(goqu.T("report_tenement").As("rt")). + Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("rt.tenement_id")))). + Select(goqu.COUNT(goqu.I("rt.*"))). + Where(goqu.I("rt.report_id").Eq(rid)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("t.full_name").ILike(pattern), + goqu.T("t.short_name").ILike(pattern), + goqu.I("t.abbr").ILike(pattern), + goqu.I("t.address").ILike(pattern), + goqu.I("t.contact_name").ILike(pattern), + goqu.I("t.contact_phone").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("t.full_name").ILike(pattern), + goqu.T("t.short_name").ILike(pattern), + goqu.I("t.abbr").ILike(pattern), + goqu.I("t.address").ILike(pattern), + goqu.I("t.contact_name").ILike(pattern), + goqu.I("t.contact_phone").ILike(pattern), + )) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery. + Order(goqu.I("t.movedin_at").Asc()). + Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + var ( + tenements []*model.ReportTenement = make([]*model.ReportTenement, 0) + count int64 + ) + querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + if err := pgxscan.Select(ctx, global.DB, &tenements, querySql, queryArgs...); err != nil { + rr.log.Error("查询指定核算报表下的商户简要计费信息时出现错误", zap.Error(err)) + return tenements, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil { + rr.log.Error("查询指定核算报表下的商户简要计费信息总数量时出现错误", zap.Error(err)) + return tenements, 0, err + } + return tenements, count, nil +} + +// 获取指定核算报表中指定商户的详细核算信息 +func (rr _ReportRepository) GetTenementDetailInReport(rid, tid string) (*model.ReportTenement, error) { + rr.log.Info("获取指定核算报表中指定商户的详细核算信息", zap.String("Report", rid), zap.String("Tenement", tid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + querySql, queryParams, _ := rr.ds. + From(goqu.T("report_tenement").As("rt")). + Select("rt.*"). + Where(goqu.I("rt.report_id").Eq(rid), goqu.I("rt.tenement_id").Eq(tid)). + Prepared(true).ToSQL() + + var tenement model.ReportTenement + if err := pgxscan.Get(ctx, global.DB, &tenement, querySql, queryParams...); err != nil { + rr.log.Error("获取指定核算报表中指定商户的详细核算信息时出现错误", zap.Error(err)) + return nil, err + } + return &tenement, nil +} + +// 更改指定核算报表的状态为已发布 +func (rr _ReportRepository) PublishReport(rid string) (bool, error) { + rr.log.Info("更改指定核算报表的状态为已发布", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + currentTime := types.Now() + updateSql, updateArgs, _ := rr.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "published": true, + "published_at": currentTime, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + res, err := global.DB.Exec(ctx, updateSql, updateArgs...) + if err != nil { + rr.log.Error("更改指定核算报表的状态为已发布时出现错误", zap.Error(err)) + return false, err + } + return res.RowsAffected() > 0, nil +} + +// 对指定核算报表进行综合检索 +func (rr _ReportRepository) ComprehensiveReportSearch(uid, pid *string, page uint, keyword *string, start, end *types.Date) ([]*model.ReportIndex, int64, error) { + rr.log.Info("对指定核算报表进行综合检索", zap.Stringp("User", uid), zap.Stringp("Park", pid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + reportQuery := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))). + LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Select("r.*", goqu.I("t.status"), goqu.I("t.message")) + countQuery := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))). + LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Select(goqu.COUNT(goqu.I("r.*"))) + + if uid != nil && len(*uid) > 0 { + reportQuery = reportQuery.Where(goqu.I("ud.id").Eq(*uid)) + countQuery = countQuery.Where(goqu.I("ud.id").Eq(*uid)) + } + + if pid != nil && len(*pid) > 0 { + reportQuery = reportQuery.Where(goqu.I("p.id").Eq(*pid)) + countQuery = countQuery.Where(goqu.I("p.id").Eq(*pid)) + } + + queryDateRange := types.NewDateRange(start, end) + reportQuery = reportQuery.Where(goqu.L("r.period <@ ?", queryDateRange)) + countQuery = countQuery.Where(goqu.L("r.period <@ ?", queryDateRange)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("p.name").ILike(pattern), + goqu.I("p.abbr").ILike(pattern), + goqu.I("p.address").ILike(pattern), + goqu.I("p.contact").ILike(pattern), + goqu.I("ud.name").ILike(pattern), + goqu.I("ud.abbr").ILike(pattern), + goqu.I("ud.contact").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("p.name").ILike(pattern), + goqu.I("p.abbr").ILike(pattern), + goqu.I("p.address").ILike(pattern), + goqu.I("p.contact").ILike(pattern), + goqu.I("ud.name").ILike(pattern), + goqu.I("ud.abbr").ILike(pattern), + goqu.I("ud.contact").ILike(pattern), + )) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery. + Order(goqu.I("r.created_at").Desc()). + Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + var ( + reports []*model.ReportIndex = make([]*model.ReportIndex, 0) + count int64 + ) + querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + if err := pgxscan.Select(ctx, global.DB, &reports, querySql, queryArgs...); err != nil { + rr.log.Error("对指定核算报表进行综合检索时出现错误", zap.Error(err)) + return reports, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil { + rr.log.Error("对指定核算报表进行综合检索总数量时出现错误", zap.Error(err)) + return reports, 0, err + } + return reports, count, nil +} + +// 判断指定报表是否是当前园区的最后一张报表 +func (rr _ReportRepository) IsLastReport(rid string) (bool, error) { + rr.log.Info("判断指定报表是否是当前园区的最后一张报表", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + checkSql, checkArgs, _ := rr.ds. + From(goqu.T("report")). + Select(goqu.COUNT("*")). + Where( + goqu.I("r.id").Eq(rid), + goqu.Func("lower", goqu.I("r.period")).Gte(rr.ds. + From(goqu.T("report").As("ri")). + Select(goqu.Func("max", goqu.Func("upper", goqu.I("ri.period")))). + Where( + goqu.I("ri.park_id").Eq(goqu.I("r.park_id")), + goqu.I("ri.id").Neq(rid), + ), + ), + goqu.I("r.published").IsTrue(), + ). + Prepared(true).ToSQL() + + var count int64 + if err := pgxscan.Get(ctx, global.DB, &count, checkSql, checkArgs...); err != nil { + rr.log.Error("判断指定报表是否是当前园区的最后一张报表时出现错误", zap.Error(err)) + return false, err + } + return count > 0, nil +} + +// 申请撤回指定的核算报表 +func (rr _ReportRepository) ApplyWithdrawReport(rid string) (bool, error) { + rr.log.Info("申请撤回指定的核算报表", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + currentTime := types.Now() + updateSql, updateArgs, _ := rr.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "withdraw": model.REPORT_WITHDRAW_APPLYING, + "last_withdraw_applied_at": currentTime, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + res, err := global.DB.Exec(ctx, updateSql, updateArgs...) + if err != nil { + rr.log.Error("申请撤回指定的核算报表时出现错误", zap.Error(err)) + return false, err + } + return res.RowsAffected() > 0, nil +} + +// 批准核算报表的撤回申请 +func (rr _ReportRepository) ApproveWithdrawReport(rid string) (bool, error) { + rr.log.Info("批准核算报表的撤回申请", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + currentTime := types.Now() + updateSql, updateArgs, _ := rr.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "withdraw": model.REPORT_WITHDRAW_GRANTED, + "last_withdraw_audit_at": currentTime, + "published": false, + "published_at": nil, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + res, err := global.DB.Exec(ctx, updateSql, updateArgs...) + if err != nil { + rr.log.Error("批准核算报表的撤回申请时出现错误", zap.Error(err)) + return false, err + } + return res.RowsAffected() > 0, nil +} + +// 拒绝核算报表的撤回申请 +func (rr _ReportRepository) RejectWithdrawReport(rid string) (bool, error) { + rr.log.Info("拒绝核算报表的撤回申请", zap.String("Report", rid)) + ctx, cancel := global.TimeoutContext() + defer cancel() + + currentTime := types.Now() + updateSql, updateArgs, _ := rr.ds. + Update(goqu.T("report")). + Set(goqu.Record{ + "withdraw": model.REPORT_WITHDRAW_DENIED, + "last_withdraw_audit_at": currentTime, + }). + Where(goqu.I("id").Eq(rid)). + Prepared(true).ToSQL() + + res, err := global.DB.Exec(ctx, updateSql, updateArgs...) + if err != nil { + rr.log.Error("拒绝核算报表的撤回申请时出现错误", zap.Error(err)) + return false, err + } + return res.RowsAffected() > 0, nil +} + +// 列出当前正在等待审核的已经申请撤回的核算报表 +func (rr _ReportRepository) ListWithdrawAppliedReports(page uint, keyword *string) ([]*model.ReportIndex, int64, error) { + rr.log.Info("列出当前正在等待审核的已经申请撤回的核算报表") + ctx, cancel := global.TimeoutContext() + defer cancel() + + reportQuery := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))). + LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Select("r.*", goqu.I("t.status"), goqu.I("t.message")). + Where(goqu.I("r.withdraw").Eq(model.REPORT_WITHDRAW_APPLYING)) + countQuery := rr.ds. + From(goqu.T("report").As("r")). + Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("r.park_id")))). + Join(goqu.T("user_detail").As("ud"), goqu.On(goqu.I("ud.id").Eq(goqu.I("p.user_id")))). + LeftJoin(goqu.T("report_task").As("t"), goqu.On(goqu.I("t.id").Eq(goqu.I("r.id")))). + Select(goqu.COUNT(goqu.I("r.*"))). + Where(goqu.I("r.withdraw").Eq(model.REPORT_WITHDRAW_APPLYING)) + + if keyword != nil && len(*keyword) > 0 { + pattern := fmt.Sprintf("%%%s%%", *keyword) + reportQuery = reportQuery.Where(goqu.Or( + goqu.I("p.name").ILike(pattern), + goqu.I("p.abbr").ILike(pattern), + goqu.I("p.address").ILike(pattern), + goqu.I("p.contact").ILike(pattern), + goqu.I("p.phone").ILike(pattern), + goqu.I("ud.name").ILike(pattern), + goqu.I("ud.abbr").ILike(pattern), + goqu.I("ud.contact").ILike(pattern), + goqu.I("ud.phone").ILike(pattern), + )) + countQuery = countQuery.Where(goqu.Or( + goqu.I("p.name").ILike(pattern), + goqu.I("p.abbr").ILike(pattern), + goqu.I("p.address").ILike(pattern), + goqu.I("p.contact").ILike(pattern), + goqu.I("p.phone").ILike(pattern), + goqu.I("ud.name").ILike(pattern), + goqu.I("ud.abbr").ILike(pattern), + goqu.I("ud.contact").ILike(pattern), + goqu.I("ud.phone").ILike(pattern), + )) + } + + startRow := (page - 1) * config.ServiceSettings.ItemsPageSize + reportQuery = reportQuery. + Order(goqu.I("r.last_withdraw_applied_at").Desc()). + Offset(startRow).Limit(config.ServiceSettings.ItemsPageSize) + + var ( + reports []*model.ReportIndex = make([]*model.ReportIndex, 0) + count int64 + ) + querySql, queryArgs, _ := reportQuery.Prepared(true).ToSQL() + countSql, countArgs, _ := countQuery.Prepared(true).ToSQL() + if err := pgxscan.Select(ctx, global.DB, &reports, querySql, queryArgs...); err != nil { + rr.log.Error("列出当前正在等待审核的已经申请撤回的核算报表时出现错误", zap.Error(err)) + return reports, 0, err + } + if err := pgxscan.Get(ctx, global.DB, &count, countSql, countArgs...); err != nil { + rr.log.Error("列出当前正在等待审核的已经申请撤回的核算报表总数量时出现错误", zap.Error(err)) + return reports, 0, err + } + return reports, count, nil +} diff --git a/vo/report.go b/vo/report.go index d685e69..d97def7 100644 --- a/vo/report.go +++ b/vo/report.go @@ -1 +1,42 @@ package vo + +import ( + "electricity_bill_calc/types" + + "github.com/shopspring/decimal" +) + +type ReportCreationForm struct { + Park string `json:"parkId"` + PeriodBegin types.Date `json:"periodBegin"` + PeriodEnd types.Date `json:"periodEnd"` + Overall decimal.Decimal `json:"overall"` + OverallFee decimal.Decimal `json:"overallFee"` + Critical decimal.Decimal `json:"critical"` + CriticalFee decimal.Decimal `json:"criticalFee"` + Peak decimal.Decimal `json:"peak"` + PeakFee decimal.Decimal `json:"peakFee"` + Flat decimal.Decimal `json:"flat,omitempty"` + FlatFee decimal.Decimal `json:"flatFee,omitempty"` + Valley decimal.Decimal `json:"valley"` + ValleyFee decimal.Decimal `json:"valleyFee"` + BasicFee decimal.Decimal `json:"basicFee"` + AdjustFee decimal.Decimal `json:"adjustFee"` +} + +type ReportModifyForm struct { + PeriodBegin types.Date `json:"periodBegin"` + PeriodEnd types.Date `json:"periodEnd"` + Overall decimal.Decimal `json:"overall"` + OverallFee decimal.Decimal `json:"overallFee"` + Critical decimal.Decimal `json:"critical"` + CriticalFee decimal.Decimal `json:"criticalFee"` + Peak decimal.Decimal `json:"peak"` + PeakFee decimal.Decimal `json:"peakFee"` + Flat decimal.Decimal `json:"flat,omitempty"` + FlatFee decimal.Decimal `json:"flatFee,omitempty"` + Valley decimal.Decimal `json:"valley"` + ValleyFee decimal.Decimal `json:"valleyFee"` + BasicFee decimal.Decimal `json:"basicFee"` + AdjustFee decimal.Decimal `json:"adjustFee"` +}