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(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(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(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) QueryAdditiionalCharges(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) 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) } f.l.Debug("Park ids", zap.Any("ids", parkIds)) f.l.Debug("Check fees", zap.Any("fees", statFees)) 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 }