Merge branch 'master' into god-mode
This commit is contained in:
commit
0ec7f5fba1
4
cache/abstract.go
vendored
4
cache/abstract.go
vendored
|
@ -3,6 +3,7 @@ package cache
|
|||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -23,9 +24,10 @@ const (
|
|||
func Cache[T interface{}](key string, value *T, expires time.Duration) error {
|
||||
var err error
|
||||
if expires > 0 {
|
||||
realExpires := expires + time.Duration(rand.Int63n(60))*time.Second
|
||||
setCmd := global.RedisConn.B().Set().
|
||||
Key(key).Value(rueidis.JSON(value)).
|
||||
ExSeconds(int64(expires.Seconds())).
|
||||
ExSeconds(int64(realExpires.Seconds())).
|
||||
Build()
|
||||
err = global.RedisConn.Do(global.Ctx, setCmd).Error()
|
||||
} else {
|
||||
|
|
55
cache/count.go
vendored
55
cache/count.go
vendored
|
@ -1,49 +1,58 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func assembleCountKey(entityName string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
return CacheKey(TAG_COUNT, keys...)
|
||||
type _CountRecord struct {
|
||||
Count int64
|
||||
}
|
||||
|
||||
func assembleCountIdentification(additional ...string) string {
|
||||
return strings.Join(additional, ":")
|
||||
func assembleCountKey(entityName string, additional ...string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
keys = append(keys, additional...)
|
||||
var b strings.Builder
|
||||
b.WriteString(TAG_COUNT)
|
||||
for _, s := range keys {
|
||||
fmt.Fprintf(&b, ":%s", s)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 向缓存中缓存模型名称明确的包含指定条件的实体记录数量
|
||||
func CacheCount(relationNames []string, entityName string, count int64, conditions ...string) error {
|
||||
countKey := assembleCountKey(entityName)
|
||||
identification := assembleCountIdentification(conditions...)
|
||||
cmd := global.RedisConn.B().Hset().Key(countKey).FieldValue().FieldValue(identification, strconv.FormatInt(count, 10)).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, cmd)
|
||||
countKey := assembleCountKey(entityName, conditions...)
|
||||
cacheInstance := &_CountRecord{Count: count}
|
||||
err := Cache(countKey, cacheInstance, 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_HASH, countKey, identification)
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, countKey)
|
||||
}
|
||||
return result.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
// 从缓存中获取模型名称明确的,包含指定条件的实体记录数量
|
||||
func RetreiveCount(entityName string, condtions ...string) (int64, error) {
|
||||
countKey := assembleCountKey(entityName)
|
||||
identification := assembleCountIdentification(condtions...)
|
||||
cmd := global.RedisConn.B().Hget().Key(countKey).Field(identification).Build()
|
||||
result, err := global.RedisConn.Do(global.Ctx, cmd).AsInt64()
|
||||
countKey := assembleCountKey(entityName, condtions...)
|
||||
exist, err := Exists(countKey)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
if !exist {
|
||||
return -1, nil
|
||||
}
|
||||
instance, err := Retreive[_CountRecord](countKey)
|
||||
if instance != nil && err == nil {
|
||||
return instance.Count, nil
|
||||
} else {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
// 删除指定模型名称的数量缓存
|
||||
func AbolishCountEntity(entityName string) error {
|
||||
countKey := assembleCountKey(entityName)
|
||||
cmd := global.RedisConn.B().Del().Key(countKey).Build()
|
||||
err := global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_COUNT, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
|
63
cache/exists.go
vendored
63
cache/exists.go
vendored
|
@ -1,72 +1,49 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func assembleExistsKey(entityName string) string {
|
||||
func assembleExistsKey(entityName string, additional ...string) string {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, strings.ToUpper(entityName))
|
||||
return CacheKey(TAG_EXISTS, keys...)
|
||||
}
|
||||
|
||||
func assembleExistsIdentification(additional ...string) string {
|
||||
return strings.Join(additional, ":")
|
||||
keys = append(keys, additional...)
|
||||
var b strings.Builder
|
||||
b.WriteString(TAG_EXISTS)
|
||||
for _, s := range keys {
|
||||
fmt.Fprintf(&b, ":%s", s)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 缓存模型名称明确的、包含指定ID以及一些附加条件的记录
|
||||
func CacheExists(relationNames []string, entityName string, conditions ...string) error {
|
||||
existskey := assembleExistsKey(entityName)
|
||||
identification := assembleExistsIdentification(conditions...)
|
||||
cmd := global.RedisConn.B().Sadd().Key(existskey).Member(identification).Build()
|
||||
err := global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
existskey := assembleExistsKey(entityName, conditions...)
|
||||
err := Cache(existskey, lo.ToPtr(true), 5*time.Minute)
|
||||
for _, relationName := range relationNames {
|
||||
CacheRelation(relationName, STORE_TYPE_SET, existskey, identification)
|
||||
CacheRelation(relationName, STORE_TYPE_KEY, existskey)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 从缓存中获取模型名称明确、包含指定ID以及一些附加条件的实体是否存在的标记,函数在返回false时不保证数据库中相关记录也不存在
|
||||
func CheckExists(entityName string, condtions ...string) (bool, error) {
|
||||
existsKey := assembleExistsKey(entityName)
|
||||
identification := assembleExistsIdentification(condtions...)
|
||||
cmd := global.RedisConn.B().Sismember().Key(existsKey).Member(identification).Build()
|
||||
result, err := global.RedisConn.Do(global.Ctx, cmd).AsBool()
|
||||
return result, err
|
||||
existsKey := assembleExistsKey(entityName, condtions...)
|
||||
return Exists(existsKey)
|
||||
}
|
||||
|
||||
// 从缓存中删除模型名称明确、包含指定ID的全部实体存在标记
|
||||
func AbolishExists(entityName, id string) error {
|
||||
existsKey := assembleExistsKey(entityName)
|
||||
pattern := fmt.Sprintf("%s*", id)
|
||||
var (
|
||||
err error
|
||||
cursor int64
|
||||
elems = make([]string, 0)
|
||||
sElem []string
|
||||
)
|
||||
for {
|
||||
cmd := global.RedisConn.B().Sscan().Key(existsKey).Cursor(cursor).Match(pattern).Count(20).Build()
|
||||
result := global.RedisConn.Do(global.Ctx, cmd)
|
||||
cursor, sElem, err = dissembleScan(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
elems = append(elems, sElem...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
cmd := global.RedisConn.B().Srem().Key(existsKey).Member(elems...).Build()
|
||||
err = global.RedisConn.Do(global.Ctx, cmd).Error()
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName), id)
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
||||
// 从缓存中删除指定模型名称的全部存在标记
|
||||
func AbolishExistsEntity(entityName string) error {
|
||||
existskey := assembleExistsKey(entityName)
|
||||
_, err := Delete(existskey)
|
||||
return err
|
||||
pattern := fmt.Sprintf("%s:%s:*", TAG_EXISTS, strings.ToUpper(entityName))
|
||||
return DeleteAll(pattern)
|
||||
}
|
||||
|
|
51
cache/relation.go
vendored
51
cache/relation.go
vendored
|
@ -2,6 +2,7 @@ package cache
|
|||
|
||||
import (
|
||||
"electricity_bill_calc/global"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/rueian/rueidis"
|
||||
|
@ -69,3 +70,53 @@ func AbolishRelation(relationName string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ClearOrphanRelationItems() error {
|
||||
var (
|
||||
err error
|
||||
cursor int64
|
||||
keys = make([]string, 0)
|
||||
sKeys []string
|
||||
)
|
||||
for {
|
||||
scanCmd := global.RedisConn.B().Scan().Cursor(cursor).Match(fmt.Sprintf("%s:*", TAG_RELATION)).Count(20).Build()
|
||||
results := global.RedisConn.Do(global.Ctx, scanCmd)
|
||||
cursor, sKeys, err = dissembleScan(results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys = append(keys, sKeys...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
var cmds = make(rueidis.Commands, 0)
|
||||
for _, key := range keys {
|
||||
relationItemsCmd := global.RedisConn.B().Smembers().Key(key).Build()
|
||||
results := global.RedisConn.Do(global.Ctx, relationItemsCmd)
|
||||
relationItems, err := results.AsStrSlice()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range relationItems {
|
||||
separated := strings.Split(item, ";")
|
||||
exist, err := Exists(separated[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
cmd := global.RedisConn.B().Srem().Key(key).Member(item).Build()
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
errs := global.RedisConn.DoMulti(global.Ctx, cmds...)
|
||||
firstErr, has := lo.Find(errs, func(elem rueidis.RedisResult) bool {
|
||||
return elem.Error() != nil
|
||||
})
|
||||
if has {
|
||||
return firstErr.Error()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
33
cache/repository.go
vendored
33
cache/repository.go
vendored
|
@ -1,33 +0,0 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/config"
|
||||
)
|
||||
|
||||
func CacheData[T interface{}](instance T, category string, key ...string) error {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
cacheKey := CacheKey("cache", keys...)
|
||||
if exists, _ := Exists(cacheKey); exists {
|
||||
Delete(cacheKey)
|
||||
}
|
||||
return Cache(cacheKey, &instance, config.ServiceSettings.CacheLifeTime)
|
||||
}
|
||||
|
||||
func RetreiveData[T interface{}](category string, key ...string) (*T, error) {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
return Retreive[T](CacheKey("cache", keys...))
|
||||
}
|
||||
|
||||
func AbolishCacheData(category string, key ...string) {
|
||||
var keys = make([]string, 0)
|
||||
keys = append(keys, category)
|
||||
keys = append(keys, key...)
|
||||
cacheKey := CacheKey("cache", keys...)
|
||||
if exists, _ := Exists(cacheKey); exists {
|
||||
Delete(cacheKey)
|
||||
}
|
||||
}
|
|
@ -49,7 +49,8 @@ func listAllParksUnderSessionUser(c *gin.Context) {
|
|||
result.Unauthorized(err.Error())
|
||||
return
|
||||
}
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid)
|
||||
keyword := c.DefaultQuery("keyword", "")
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(userSession.Uid, keyword)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
|
@ -60,7 +61,8 @@ func listAllParksUnderSessionUser(c *gin.Context) {
|
|||
func listAllParksUnderSpecificUser(c *gin.Context) {
|
||||
result := response.NewResult(c)
|
||||
requestUserId := c.Param("uid")
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId)
|
||||
keyword := c.DefaultQuery("keyword", "")
|
||||
parks, err := service.ParkService.ListAllParkBelongsTo(requestUserId, keyword)
|
||||
if err != nil {
|
||||
result.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
|
@ -76,7 +78,7 @@ type _ParkInfoFormData struct {
|
|||
Phone *string `json:"phone" from:"phone"`
|
||||
Area decimal.NullDecimal `json:"area" from:"area"`
|
||||
Capacity decimal.NullDecimal `json:"capacity" from:"capacity"`
|
||||
Tenement decimal.NullDecimal `json:"tenement" from:"tenement"`
|
||||
TenementQuantity decimal.NullDecimal `json:"tenement" from:"tenement"`
|
||||
Category int `json:"category" form:"category"`
|
||||
Submeter int `json:"submeter" form:"submeter"`
|
||||
}
|
||||
|
|
13
main.go
13
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"electricity_bill_calc/cache"
|
||||
"electricity_bill_calc/config"
|
||||
"electricity_bill_calc/global"
|
||||
"electricity_bill_calc/model"
|
||||
|
@ -157,8 +158,20 @@ func DBConnectionKeepLive() {
|
|||
}
|
||||
}
|
||||
|
||||
func RedisOrphanCleanup() {
|
||||
for range time.Tick(2 * time.Minute) {
|
||||
log.Printf("[Cache] [Cleanup] Proceeding cleanup orphan keys.")
|
||||
err := cache.ClearOrphanRelationItems()
|
||||
if err != nil {
|
||||
log.Printf("[Cache] [Cleanup] Orphan keys clear failed: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go DBConnectionKeepLive()
|
||||
go RedisOrphanCleanup()
|
||||
gin.SetMode(config.ServerSettings.RunMode)
|
||||
r := router.Router()
|
||||
r.Run(fmt.Sprintf(":%d", config.ServerSettings.HttpPort))
|
||||
|
|
|
@ -205,7 +205,7 @@ func (_ChargeService) ListPagedChargeRecord(keyword, beginDate, endDate string,
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cachedTotal, _ := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 {
|
||||
if cachedTotal, err := cache.RetreiveCount("charge_with_name", condition...); cachedTotal != -1 && err == nil {
|
||||
total = cachedTotal
|
||||
} else {
|
||||
total, err = global.DBConn.
|
||||
|
|
|
@ -45,7 +45,7 @@ func (_EndUserService) SearchEndUserRecord(reportId, keyword string, page int) (
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cachedTotal, _ := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 {
|
||||
if cachedTotal, err := cache.RetreiveCount("end_user_detail", conditions...); cachedTotal != -1 && err == nil {
|
||||
total = cachedTotal
|
||||
} else {
|
||||
total, err = global.DBConn.
|
||||
|
|
|
@ -36,7 +36,7 @@ func (_Meter04kVService) ListMeterDetail(park, keyword string, page int) ([]mode
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cachedTotal, _ := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 {
|
||||
if cachedTotal, err := cache.RetreiveCount("meter_04kv", condition...); cachedTotal != -1 && err == nil {
|
||||
total = cachedTotal
|
||||
} else {
|
||||
total, err = global.DBConn.Where(cond).NoAutoCondition().Count(new(model.Meter04KV))
|
||||
|
|
|
@ -73,19 +73,29 @@ func (_ParkService) DeletePark(uid, pid string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (_ParkService) ListAllParkBelongsTo(uid string) ([]model.Park, error) {
|
||||
if parks, _ := cache.RetreiveSearch[[]model.Park]("park", "belong", uid); parks != 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
|
||||
}
|
||||
cond := builder.NewCond().And(builder.Eq{"user_id": uid})
|
||||
if len(keyword) > 0 {
|
||||
cond = cond.And(
|
||||
builder.Like{"name", keyword}.
|
||||
Or(builder.Like{"abbr", keyword}).
|
||||
Or(builder.Like{"address", keyword}).
|
||||
Or(builder.Like{"contact", keyword}).
|
||||
Or(builder.Like{"phone", keyword}),
|
||||
)
|
||||
}
|
||||
parks := make([]model.Park, 0)
|
||||
err := global.DBConn.
|
||||
Where(builder.Eq{"user_id": uid}).
|
||||
Where(cond).
|
||||
NoAutoCondition().
|
||||
Find(&parks)
|
||||
if err != nil {
|
||||
return make([]model.Park, 0), err
|
||||
}
|
||||
cache.CacheSearch(parks, []string{"park"}, "park", "belong", uid)
|
||||
cache.CacheSearch(parks, []string{"park"}, "park", "belong", uid, keyword)
|
||||
return parks, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ func (_ReportService) CalculateSummaryAndFinishStep(reportId string) error {
|
|||
}
|
||||
defer tx.Close()
|
||||
summary.CalculatePrices()
|
||||
_, err = tx.ID(summary.ReportId).Cols("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price").Update(summary)
|
||||
_, err = tx.ID(summary.ReportId).Cols("overall_price", "critical_price", "peak_price", "flat", "flat_fee", "flat_price", "valley_price", "consumption_fee").Update(summary)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
|
@ -461,7 +461,7 @@ func (_ReportService) SearchReport(requestUser, requestPark, requestKeyword stri
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cachedTotal, _ := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 {
|
||||
if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil {
|
||||
total = cachedTotal
|
||||
} else {
|
||||
total, err := global.DBConn.
|
||||
|
|
|
@ -14,7 +14,7 @@ type _StatisticsService struct{}
|
|||
var StatisticsService _StatisticsService
|
||||
|
||||
func (_StatisticsService) EnabledEnterprises() (int64, error) {
|
||||
if cachedCount, _ := cache.RetreiveCount("enabled_ent"); cachedCount != -1 {
|
||||
if cachedCount, err := cache.RetreiveCount("enabled_ent"); cachedCount != -1 && err == nil {
|
||||
return cachedCount, nil
|
||||
}
|
||||
c, err := global.DBConn.
|
||||
|
@ -28,7 +28,7 @@ func (_StatisticsService) EnabledEnterprises() (int64, error) {
|
|||
}
|
||||
|
||||
func (_StatisticsService) EnabledParks(userIds ...string) (int64, error) {
|
||||
if cachedParks, _ := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 {
|
||||
if cachedParks, err := cache.RetreiveCount("enabled_parks", userIds...); cachedParks != -1 && err == nil {
|
||||
return cachedParks, nil
|
||||
}
|
||||
cond := builder.NewCond().And(builder.Eq{"enabled": true})
|
||||
|
|
|
@ -342,7 +342,7 @@ func (_UserService) ListUserDetail(keyword string, userType int, userState *bool
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cacheCounts, _ := cache.RetreiveCount("join_user_detail", cacheConditions...); cacheCounts != -1 {
|
||||
if cacheCounts, err := cache.RetreiveCount("join_user_detail", cacheConditions...); cacheCounts != -1 && err == nil {
|
||||
total = cacheCounts
|
||||
} else {
|
||||
total, err = global.DBConn.
|
||||
|
|
|
@ -85,7 +85,7 @@ func (_WithdrawService) FetchPagedWithdrawApplies(page int, keyword string) ([]m
|
|||
total int64
|
||||
err error
|
||||
)
|
||||
if cachedTotal, _ := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 {
|
||||
if cachedTotal, err := cache.RetreiveCount("join_report_for_withdraw", conditions...); cachedTotal != -1 && err == nil {
|
||||
total = cachedTotal
|
||||
} else {
|
||||
total, err = global.DBConn.
|
||||
|
@ -134,7 +134,7 @@ func (_WithdrawService) AuditWithdraw(reportId string, granted bool) error {
|
|||
}
|
||||
|
||||
func (_WithdrawService) AuditWaits() (int64, error) {
|
||||
if cachedWaits, _ := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 {
|
||||
if cachedWaits, err := cache.RetreiveCount("withdraw_waits"); cachedWaits != -1 && err == nil {
|
||||
return cachedWaits, nil
|
||||
}
|
||||
cond := builder.NewCond()
|
||||
|
|
Loading…
Reference in New Issue
Block a user