forked from free-lancers/electricity_bill_calc_service
refactor(changes):暂时删除全部内容,并完成基本数据库连接的创建。
This commit is contained in:
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
@@ -1,295 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"fmt"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/samber/lo"
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/uptrace/bun"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _MaintenanceFeeService struct {
|
||||
l *zap.Logger
|
||||
}
|
||||
|
||||
var MaintenanceFeeService = _MaintenanceFeeService{
|
||||
l: logger.Named("Service", "maintenance"),
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) ListMaintenanceFees(pid []string, period string, requestPage int) ([]model.MaintenanceFee, int64, error) {
|
||||
conditions := []string{fmt.Sprintf("%d", requestPage)}
|
||||
conditions = append(conditions, pid...)
|
||||
conditions = append(conditions, period)
|
||||
if cachedTotal, err := cache.RetreiveCount("maintenance_fee", conditions...); cachedTotal != -1 && err == nil {
|
||||
if fees, _ := cache.RetreiveSearch[[]model.MaintenanceFee]("maintenance_fee", conditions...); fees != nil {
|
||||
return *fees, cachedTotal, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
fees = make([]model.MaintenanceFee, 0)
|
||||
cond = global.DB.NewSelect().Model(&fees)
|
||||
)
|
||||
if len(pid) > 0 {
|
||||
cond = cond.Where("park_id in (?)", bun.In(pid))
|
||||
} else {
|
||||
return make([]model.MaintenanceFee, 0), 0, exceptions.NewIllegalArgumentsError("必须给定所要请求的至少一个园区", "park_id")
|
||||
}
|
||||
if len(period) > 0 {
|
||||
cond = cond.Where("period = ?", period)
|
||||
}
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
|
||||
total, err := cond.Order("period desc", "created_at desc").
|
||||
Limit(config.ServiceSettings.ItemsPageSize).
|
||||
Offset(startItem).
|
||||
ScanAndCount(ctx)
|
||||
if err != nil {
|
||||
return make([]model.MaintenanceFee, 0), 0, fmt.Errorf("附加费查询出现错误,%w", err)
|
||||
}
|
||||
relations := lo.Map(fees, func(f model.MaintenanceFee, _ int) string {
|
||||
return fmt.Sprintf("maintenance_fee:%s", f.Id)
|
||||
})
|
||||
relations = append(relations, "maintenance_fee", "park")
|
||||
|
||||
cache.CacheCount(relations, "maintenance_fee", int64(total), conditions...)
|
||||
cache.CacheSearch(fees, relations, "maintenance_fee", conditions...)
|
||||
return fees, int64(total), nil
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) CreateMaintenanceFeeRecord(fee model.MaintenanceFee) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
fee.Id = uuid.New().String()
|
||||
fee.Enabled = true
|
||||
_, err := global.DB.NewInsert().Model(&fee).Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.AbolishRelation("maintenance_fee")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) ModifyMaintenanceFee(fee model.MaintenanceFee) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
res, err := global.DB.NewUpdate().Model(&fee).
|
||||
WherePK().
|
||||
Column("fee", "memo").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, _ := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation("maintenance_fee")
|
||||
cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fee.Id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) ChangeMaintenanceFeeState(fid string, state bool) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
res, err := global.DB.NewUpdate().Model((*model.MaintenanceFee)(nil)).
|
||||
Where("id = ?", fid).
|
||||
Set("enabled = ?", state).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, err := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation("maintenance_fee")
|
||||
cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) DeleteMaintenanceFee(fid string) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
res, err := global.DB.NewDelete().Model((*model.MaintenanceFee)(nil)).
|
||||
Where("id = ?", fid).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, err := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到匹配的附加费记录。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation("maintenance_fee")
|
||||
cache.AbolishRelation(fmt.Sprintf("maintenance_fee:%s", fid))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_MaintenanceFeeService) EnsureFeeBelongs(uid, mid string) (bool, error) {
|
||||
if has, _ := cache.CheckExists("maintenance_fee", mid, uid); has {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
parks := make([]model.Park, 0)
|
||||
err := global.DB.NewSelect().Model(&parks).
|
||||
Relation("MaintenanceFees").
|
||||
Where("p.user_id = ?", uid).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
exists := lo.Reduce(parks, func(acc bool, elem model.Park, _ int) bool {
|
||||
for _, e := range elem.MaintenanceFees {
|
||||
if e.Id == mid {
|
||||
return acc || true
|
||||
}
|
||||
}
|
||||
return acc || false
|
||||
}, false)
|
||||
if !exists {
|
||||
return false, exceptions.NewNotFoundError("指定附加费所属园区未找到。")
|
||||
}
|
||||
cache.CacheExists([]string{fmt.Sprintf("maintenance_fee:%s", mid), "maintenance_fee", "park"}, "maintenance_fee", mid, uid)
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
type _FeeStat struct {
|
||||
ParkId string
|
||||
Period string
|
||||
Total decimal.Decimal
|
||||
}
|
||||
|
||||
func (f _MaintenanceFeeService) QueryAdditionalCharges(uid, pid, period, keyword string, requestPage int) ([]model.AdditionalCharge, int64, error) {
|
||||
var (
|
||||
conditions = []string{fmt.Sprintf("%d", requestPage)}
|
||||
statFees = make([]_FeeStat, 0)
|
||||
cond = global.DB.NewSelect().
|
||||
Model((*model.MaintenanceFee)(nil)).
|
||||
Relation("Park", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.ExcludeColumn("*")
|
||||
}).
|
||||
Relation("Park.Enterprise", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.ExcludeColumn("*")
|
||||
}).
|
||||
Where("m.enabled = ?", true)
|
||||
)
|
||||
if len(uid) > 0 {
|
||||
cond = cond.Where("park__enterprise.id = ?", uid)
|
||||
conditions = append(conditions, uid)
|
||||
} else {
|
||||
conditions = append(conditions, "_")
|
||||
}
|
||||
if len(pid) > 0 {
|
||||
cond = cond.Where("park.id = ?", pid)
|
||||
conditions = append(conditions, pid)
|
||||
} else {
|
||||
conditions = append(conditions, "_")
|
||||
}
|
||||
if len(period) > 0 {
|
||||
cond = cond.Where("m.period = ?", period)
|
||||
conditions = append(conditions, period)
|
||||
} else {
|
||||
conditions = append(conditions, "_")
|
||||
}
|
||||
if len(keyword) > 0 {
|
||||
keywordCond := "%" + keyword + "%"
|
||||
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("park__enterprise.name like ?", keywordCond).
|
||||
WhereOr("park__enterprise.abbr like ?", keywordCond).
|
||||
WhereOr("park.name like ?", keywordCond).
|
||||
WhereOr("park.abbr like ?", keywordCond).
|
||||
WhereOr("park.address like ?", keywordCond)
|
||||
})
|
||||
conditions = append(conditions, keyword)
|
||||
} else {
|
||||
conditions = append(conditions, "_")
|
||||
}
|
||||
|
||||
if cachedTotal, err := cache.RetreiveCount("additional_charge", conditions...); cachedTotal != -1 && err == nil {
|
||||
if cachedData, _ := cache.RetreiveSearch[[]model.AdditionalCharge]("additional_charge", conditions...); cachedData != nil {
|
||||
return *cachedData, cachedTotal, nil
|
||||
}
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext(24)
|
||||
defer cancel()
|
||||
|
||||
startItem := (requestPage - 1) * config.ServiceSettings.ItemsPageSize
|
||||
|
||||
total, err := cond.ColumnExpr("sum(?) as total", bun.Ident("fee")).
|
||||
Column("park_id", "period").
|
||||
Group("park_id", "period").
|
||||
Order("period desc").
|
||||
Limit(config.ServiceSettings.ItemsPageSize).
|
||||
Offset(startItem).
|
||||
ScanAndCount(ctx, &statFees)
|
||||
if err != nil {
|
||||
return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取附加费统计信息出现错误,%w", err)
|
||||
}
|
||||
parkIds := lo.Reduce(
|
||||
statFees,
|
||||
func(acc mapset.Set[string], elem _FeeStat, _ int) mapset.Set[string] {
|
||||
acc.Add(elem.ParkId)
|
||||
return acc
|
||||
},
|
||||
mapset.NewSet[string](),
|
||||
)
|
||||
parks := make([]model.Park, 0)
|
||||
if len(parkIds.ToSlice()) > 0 {
|
||||
err = global.DB.NewSelect().Model(&parks).Relation("Enterprise").
|
||||
Where("p.id in (?)", bun.In(parkIds.ToSlice())).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return make([]model.AdditionalCharge, 0), 0, fmt.Errorf("获取园区信息出现错误,%w", err)
|
||||
}
|
||||
}
|
||||
assembledStat := lo.Reduce(
|
||||
statFees,
|
||||
func(acc []model.AdditionalCharge, elem _FeeStat, _ int) []model.AdditionalCharge {
|
||||
park, has := lo.Find(parks, func(p model.Park) bool {
|
||||
return p.Id == elem.ParkId
|
||||
})
|
||||
f.l.Debug("Park detection.", zap.Bool("has", has), zap.Any("park", park))
|
||||
if has {
|
||||
if !park.Area.Valid || park.Area.Decimal.Equal(decimal.Zero) {
|
||||
return acc
|
||||
}
|
||||
price := elem.Total.Div(park.Area.Decimal).RoundBank(8)
|
||||
return append(acc, model.AdditionalCharge{
|
||||
ParkId: elem.ParkId,
|
||||
Period: elem.Period,
|
||||
Fee: elem.Total,
|
||||
Price: price,
|
||||
QuarterPrice: price.Div(decimal.NewFromInt(4)),
|
||||
SemiAnnualPrice: price.Div(decimal.NewFromInt(2)),
|
||||
Enterprise: model.FromUserDetail(*park.Enterprise),
|
||||
Park: park,
|
||||
})
|
||||
} else {
|
||||
return acc
|
||||
}
|
||||
},
|
||||
make([]model.AdditionalCharge, 0),
|
||||
)
|
||||
cache.CacheCount([]string{"maintenance_fee"}, "additional_charge", int64(total), conditions...)
|
||||
cache.CacheSearch(assembledStat, []string{"maintenance_fee"}, "additional_charge", conditions...)
|
||||
return assembledStat, int64(total), nil
|
||||
}
|
@@ -1,231 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/excel"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/uptrace/bun"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _Meter04kVService struct {
|
||||
l *zap.Logger
|
||||
}
|
||||
|
||||
var Meter04kVService = _Meter04kVService{
|
||||
l: logger.Named("Service", "Meter04KV"),
|
||||
}
|
||||
|
||||
func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]model.Meter04KV, int64, error) {
|
||||
var (
|
||||
condition = make([]string, 0)
|
||||
meters = make([]model.Meter04KV, 0)
|
||||
)
|
||||
cond := global.DB.NewSelect().Model(&meters).
|
||||
Where("park_id = ?", park)
|
||||
condition = append(condition, park, strconv.Itoa(page))
|
||||
if len(keyword) > 0 {
|
||||
keywordCond := "%" + keyword + "%"
|
||||
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("address like ?", keywordCond).
|
||||
WhereOr("code like ?", keywordCond).
|
||||
WhereOr("customer_name like ?", keywordCond).
|
||||
WhereOr("contact_name like ?", keywordCond).
|
||||
WhereOr("contact_phone like ?", keywordCond)
|
||||
})
|
||||
condition = append(condition, keyword)
|
||||
}
|
||||
if cachedTotal, err := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 && err == nil {
|
||||
if cachedMeters, _ := cache.RetreiveSearch[[]model.Meter04KV]("meter_04kv", condition...); cachedMeters != nil {
|
||||
return *cachedMeters, cachedTotal, nil
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
startItem := (page - 1) * config.ServiceSettings.ItemsPageSize
|
||||
total, err := cond.
|
||||
Order("seq asc", "code asc").
|
||||
Limit(config.ServiceSettings.ItemsPageSize).
|
||||
Offset(startItem).
|
||||
ScanAndCount(ctx)
|
||||
|
||||
relations := lo.Map(meters, func(m model.Meter04KV, _ int) string {
|
||||
return fmt.Sprintf("meter_04kv:%s:%s", m.ParkId, m.Code)
|
||||
})
|
||||
relations = append(relations, "meter_04kv", "park")
|
||||
|
||||
cache.CacheCount(relations, "meter_04kv", int64(total), condition...)
|
||||
cache.CacheSearch(meters, relations, "meter_04kv", condition...)
|
||||
return meters, int64(total), err
|
||||
}
|
||||
|
||||
func (_Meter04kVService) Get04kVMeterDetail(park, code string) (*model.Meter04KV, error) {
|
||||
if cachedMeter, _ := cache.RetreiveEntity[model.Meter04KV]("meter_04kv", fmt.Sprintf("%s_%s", park, code)); cachedMeter != nil {
|
||||
return cachedMeter, nil
|
||||
}
|
||||
var meter = new(model.Meter04KV)
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
err := global.DB.NewSelect().Model(meter).
|
||||
Where("code = ?", code).
|
||||
Where("park_id = ?", park).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cache.CacheEntity(meter, []string{fmt.Sprintf("meter_04kv:%s:%s", park, code), "park"}, "meter_04kv", fmt.Sprintf("%s_%s", park, code))
|
||||
return meter, nil
|
||||
}
|
||||
|
||||
func (_Meter04kVService) insertNewMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error {
|
||||
_, err := tx.NewInsert().Model(&meter).Exec(*ctx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
cache.AbolishRelation("meter_04kv")
|
||||
return err
|
||||
}
|
||||
|
||||
func (_Meter04kVService) updateMeter(tx *bun.Tx, ctx *context.Context, meter model.Meter04KV) error {
|
||||
_, err := tx.NewUpdate().Model(&meter).
|
||||
Where("code = ?", meter.Code).
|
||||
Where("park_id = ?", meter.ParkId).
|
||||
Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled").
|
||||
Exec(*ctx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
|
||||
return err
|
||||
}
|
||||
|
||||
func (m _Meter04kVService) CreateSingleMeter(meter model.Meter04KV) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.insertNewMeter(&tx, &ctx, meter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
cache.AbolishRelation("meter_04kv")
|
||||
cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m _Meter04kVService) UpdateSingleMeter(meter *model.Meter04KV) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.updateMeter(&tx, &ctx, *meter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
cache.AbolishRelation(fmt.Sprintf("meter_04kv:%s:%s", meter.ParkId, meter.Code))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_Meter04kVService) DuplicateMeterCodeValidate(meters []model.Meter04KV) []excel.ExcelAnalysisError {
|
||||
errs := make([]excel.ExcelAnalysisError, 0)
|
||||
for i := 0; i < len(meters); i++ {
|
||||
for j := i + 1; j < len(meters); j++ {
|
||||
if meters[j].Code == meters[i].Code {
|
||||
errs = append(errs, excel.ExcelAnalysisError{Row: j + 1, Col: 0, Err: excel.AnalysisError{Err: fmt.Errorf("第 %d 行表计表号与第 %d 行表计表号重复!", j+1, i+1)}})
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (m _Meter04kVService) BatchCreateMeter(meters []model.Meter04KV) error {
|
||||
parkIds := lo.Reduce(meters, func(acc mapset.Set[string], elem model.Meter04KV, index int) mapset.Set[string] {
|
||||
acc.Add(elem.ParkId)
|
||||
return acc
|
||||
}, mapset.NewSet[string]())
|
||||
if parkIds.Cardinality() > 1 {
|
||||
return fmt.Errorf("一次只能向同一个园区中添加0.4kV表计。")
|
||||
}
|
||||
parkId, _ := parkIds.Pop()
|
||||
|
||||
ctx, cancel := global.TimeoutContext(120)
|
||||
defer cancel()
|
||||
|
||||
allMeterCodes := make([]string, 0)
|
||||
err := global.DB.NewSelect().Model((*model.Meter04KV)(nil)).
|
||||
Where("park_id = ?", parkId).
|
||||
Column("code").
|
||||
Scan(ctx, &allMeterCodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
meterCodes := mapset.NewSet(allMeterCodes...)
|
||||
|
||||
tx, err := global.DB.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
updates = make([]model.Meter04KV, 0)
|
||||
inserts = make([]model.Meter04KV, 0)
|
||||
)
|
||||
for _, meter := range meters {
|
||||
if meterCodes.Contains(meter.Code) {
|
||||
updates = append(updates, meter)
|
||||
} else {
|
||||
inserts = append(inserts, meter)
|
||||
}
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
_, err = tx.NewUpdate().Model(&updates).
|
||||
Column("address", "customer_name", "contact_name", "contact_phone", "ratio", "seq", "public_meter", "enabled").
|
||||
Bulk().
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(inserts) > 0 {
|
||||
_, err = tx.NewInsert().Model(&inserts).Exec(ctx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
cache.AbolishRelation("meter_04kv")
|
||||
return nil
|
||||
}
|
171
service/park.go
171
service/park.go
@@ -1,171 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/exceptions"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"fmt"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _ParkService struct {
|
||||
l *zap.Logger
|
||||
}
|
||||
|
||||
var ParkService = _ParkService{
|
||||
l: logger.Named("Service", "Park"),
|
||||
}
|
||||
|
||||
func (_ParkService) SaveNewPark(park model.Park) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
_, err := global.DB.NewInsert().Model(&park).Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.AbolishRelation("park")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ParkService) UpdateParkInfo(park *model.Park) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
res, err := global.DB.NewUpdate().Model(park).
|
||||
Where("id = ?", park.Id).
|
||||
Where("user_id = ?", park.UserId).
|
||||
Column("name", "abbr", "region", "area", "address", "contact", "phone", "capacity", "tenement_quantity", "category", "meter_04kv_type").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, _ := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到符合条件的园区。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation(fmt.Sprintf("park:%s", park.Id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ParkService) ChangeParkState(uid, pid string, state bool) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
res, err := global.DB.NewUpdate().Model((*model.Park)(nil)).
|
||||
Where("id = ?", pid).
|
||||
Where("user_id = ?", uid).
|
||||
Set("enabled = ?", state).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, _ := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到符合条件的园区。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation(fmt.Sprintf("park:%s", pid))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ParkService) DeletePark(uid, pid string) error {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
res, err := global.DB.NewDelete().Model((*model.Park)(nil)).
|
||||
Where("id = ?", pid).
|
||||
Where("user_id = ?", uid).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
if rows, _ := res.RowsAffected(); rows == 0 {
|
||||
return exceptions.NewNotFoundError("未能找到符合条件的园区。")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cache.AbolishRelation("park")
|
||||
cache.AbolishRelation(fmt.Sprintf("park:%s", pid))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ParkService) ListAllParkBelongsTo(uid, keyword string) ([]model.Park, error) {
|
||||
if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid, keyword); parks != nil {
|
||||
return *parks, nil
|
||||
}
|
||||
parks := make([]model.Park, 0)
|
||||
cond := global.DB.NewSelect().Model(&parks).
|
||||
Where("user_id = ?", uid)
|
||||
if len(keyword) > 0 {
|
||||
keywordCond := "%" + keyword + "%"
|
||||
cond = cond.WhereGroup(" and ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("name like ?", keywordCond).
|
||||
WhereOr("abbr like ?", keywordCond).
|
||||
WhereOr("address like ?", keywordCond).
|
||||
WhereOr("contact like ?", keywordCond).
|
||||
WhereOr("phone like ?", keywordCond)
|
||||
})
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
err := cond.Scan(ctx)
|
||||
if err != nil {
|
||||
return make([]model.Park, 0), err
|
||||
}
|
||||
relations := []string{"park"}
|
||||
for _, p := range parks {
|
||||
relations = append(relations, fmt.Sprintf("park:%s", p.Id))
|
||||
}
|
||||
cache.CacheSearch(parks, relations, "park", "belong", uid, keyword)
|
||||
return parks, nil
|
||||
}
|
||||
|
||||
func (_ParkService) FetchParkDetail(pid string) (*model.Park, error) {
|
||||
if park, _ := cache.RetreiveEntity[model.Park]("park", pid); park != nil {
|
||||
return park, nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
var park = new(model.Park)
|
||||
err := global.DB.NewSelect().Model(park).
|
||||
Where("id = ?", pid).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, exceptions.NewNotFoundErrorFromError("未找到符合条件的园区记录。", err)
|
||||
}
|
||||
cache.CacheEntity(*park, []string{fmt.Sprintf("park:%s", pid)}, "park", pid)
|
||||
return park, nil
|
||||
}
|
||||
|
||||
func (_ParkService) EnsurePark(uid, pid string) (bool, error) {
|
||||
if has, _ := cache.CheckExists("park", pid, uid); has {
|
||||
return has, nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
has, err := global.DB.NewSelect().Model((*model.Park)(nil)).
|
||||
Where("id = ?", pid).
|
||||
Where("user_id = ?", uid).
|
||||
Exists(ctx)
|
||||
if has {
|
||||
cache.CacheExists([]string{fmt.Sprintf("park:%s", pid)}, "park", pid, uid)
|
||||
}
|
||||
return has, err
|
||||
}
|
||||
|
||||
func (_ParkService) AllParkIds(uid string) ([]string, error) {
|
||||
if ids, _ := cache.RetreiveSearch[[]string]("park", "belong", uid); ids != nil {
|
||||
return *ids, nil
|
||||
}
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
var ids = make([]string, 0)
|
||||
err := global.DB.NewSelect().Model((*model.Park)(nil)).
|
||||
Where("user_id = ?", uid).
|
||||
Column("id").
|
||||
Scan(ctx, &ids)
|
||||
if err != nil {
|
||||
return make([]string, 0), err
|
||||
}
|
||||
cache.CacheSearch(ids, []string{"park"}, "park", "belong", uid)
|
||||
return ids, nil
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/logger"
|
||||
"electricity_bill_calc/model"
|
||||
"fmt"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type _RegionService struct {
|
||||
l *zap.Logger
|
||||
}
|
||||
|
||||
var RegionService = _RegionService{
|
||||
l: logger.Named("Service", "Region"),
|
||||
}
|
||||
|
||||
func (_RegionService) FetchSubRegions(parent string) ([]model.Region, error) {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
if regions, _ := cache.RetreiveSearch[[]model.Region]("region", "parent", parent); regions != nil {
|
||||
return *regions, nil
|
||||
}
|
||||
regions := make([]model.Region, 0)
|
||||
err := global.DB.NewSelect().Model(®ions).Where("parent = ?", parent).Order("code asc").Scan(ctx)
|
||||
if err != nil {
|
||||
return make([]model.Region, 0), err
|
||||
}
|
||||
relationNames := lo.Map(regions, func(r model.Region, index int) string {
|
||||
return fmt.Sprintf("region:%s", r.Code)
|
||||
})
|
||||
cache.CacheSearch(regions, relationNames, "region", "parent", parent)
|
||||
return regions, err
|
||||
}
|
||||
|
||||
func (r _RegionService) FetchAllParentRegions(code string) ([]model.Region, error) {
|
||||
regions := make([]model.Region, 0)
|
||||
region, err := r.fetchRegion(code)
|
||||
if err != nil {
|
||||
return regions, err
|
||||
}
|
||||
regions = append(regions, *region)
|
||||
for region.Level > 1 {
|
||||
region, err = r.fetchRegion(region.Parent)
|
||||
if err != nil {
|
||||
return make([]model.Region, 0), nil
|
||||
}
|
||||
regions = append(regions, *region)
|
||||
}
|
||||
return regions, nil
|
||||
}
|
||||
|
||||
func (_RegionService) fetchRegion(code string) (*model.Region, error) {
|
||||
ctx, cancel := global.TimeoutContext()
|
||||
defer cancel()
|
||||
if cachedRegion, _ := cache.RetreiveSearch[model.Region]("region", code); cachedRegion != nil {
|
||||
return cachedRegion, nil
|
||||
}
|
||||
region := new(model.Region)
|
||||
err := global.DB.NewSelect().Model(region).Where("code = ?", code).Scan(ctx)
|
||||
if err != nil {
|
||||
relationName := fmt.Sprintf("region:%s", code)
|
||||
cache.CacheSearch(region, []string{relationName}, "region", code)
|
||||
}
|
||||
return region, err
|
||||
}
|
@@ -1,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
|
||||
}
|
@@ -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
|
||||
}
|
447
service/user.go
447
service/user.go
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
Reference in New Issue
Block a user