26 changed files with 310 additions and 59 deletions
-
20api/vote_record/v1/vote_record.go
-
1api/vote_record/vote_record.go
-
2hack/config.yaml
-
11internal/consts/consts.go
-
12internal/controller/vote_record/vote_record_v1_get_export_file.go
-
2internal/dao/internal/article.go
-
8internal/dao/internal/user.go
-
4internal/dao/internal/vote_poll.go
-
10internal/dao/internal/vote_record.go
-
21internal/logic/vote_record/add_record.go
-
87internal/logic/vote_record/get_export_file.go
-
30internal/logic/vote_record/get_vote.go
-
36internal/logic/vote_record/get_vote_detail.go
-
2internal/model/do/article.go
-
7internal/model/do/user.go
-
2internal/model/do/vote_poll.go
-
8internal/model/do/vote_record.go
-
2internal/model/entity/article.go
-
15internal/model/entity/user.go
-
2internal/model/entity/vote_poll.go
-
14internal/model/entity/vote_record.go
-
1internal/service/vote_record.go
-
13internal/utils/cachettl.go
-
45internal/utils/delete_keys.go
-
2main.go
-
12manifest/config/config.yaml
@ -1 +1,12 @@ |
|||
package consts |
|||
|
|||
const ( |
|||
BASE_TTL = 600 |
|||
RANDOM_RANGE = 60 |
|||
) |
|||
|
|||
const ( |
|||
VOTE_DETAIL = "voteDetail" //投票名单查询
|
|||
ARTICLE = "article" //文章查询
|
|||
EXPORT_FILE = "exportFile" //导出数据查询
|
|||
) |
@ -0,0 +1,12 @@ |
|||
package vote_record |
|||
|
|||
import ( |
|||
"context" |
|||
"practice_ArticleVote_Go/internal/service" |
|||
|
|||
"practice_ArticleVote_Go/api/vote_record/v1" |
|||
) |
|||
|
|||
func (c *ControllerV1) GetExportFile(ctx context.Context, req *v1.GetExportFileReq) (res *v1.GetExportFileRes, err error) { |
|||
return service.NewVoteRecordService().ExportVoteDetail(ctx, req) |
|||
} |
@ -0,0 +1,87 @@ |
|||
package vote_record |
|||
|
|||
import ( |
|||
"context" |
|||
"encoding/json" |
|||
"fmt" |
|||
"github.com/gogf/gf/v2/frame/g" |
|||
v1 "practice_ArticleVote_Go/api/vote_record/v1" |
|||
"practice_ArticleVote_Go/internal/consts" |
|||
"practice_ArticleVote_Go/internal/utils" |
|||
) |
|||
|
|||
func (l *VoteRecordLogic) ExportVoteDetail(ctx context.Context, req *v1.GetExportFileReq) (res *v1.GetExportFileRes, err error) { |
|||
redis := g.Redis() |
|||
cacheKey := fmt.Sprintf( |
|||
consts.EXPORT_FILE+":%d:%s:%s:%s:%s", |
|||
req.VoteId, req.Username, req.Account, req.Area, req.OptionContent, |
|||
) |
|||
data, _ := redis.Get(ctx, cacheKey) |
|||
fmt.Println("data", data) |
|||
if data != nil { |
|||
var cached v1.GetExportFileRes |
|||
err = json.Unmarshal([]byte(data.String()), &cached) |
|||
if err == nil { |
|||
return &cached, nil |
|||
} |
|||
} |
|||
query := ` |
|||
SELECT |
|||
u.username, |
|||
u.account, |
|||
u.area, |
|||
uv.option_contents, |
|||
uv.create_time |
|||
FROM ( |
|||
SELECT |
|||
vr.user_id, |
|||
vr.vote_index, |
|||
DATE(vr.create_time) AS vote_date, |
|||
MAX(vr.create_time) AS create_time, |
|||
GROUP_CONCAT(o.option_content ORDER BY o.id SEPARATOR ',') AS option_contents |
|||
FROM vote_record vr |
|||
JOIN vote_option o |
|||
ON vr.option_id = o.id |
|||
WHERE vr.vote_id = ? |
|||
GROUP BY vr.user_id, vr.vote_index, vote_date |
|||
) AS uv |
|||
JOIN user u ON u.id = uv.user_id |
|||
WHERE 1=1 |
|||
` |
|||
|
|||
conditions := []interface{}{req.VoteId} |
|||
|
|||
if req.OptionContent != "" { |
|||
query += " AND uv.option_contents LIKE ?" |
|||
conditions = append(conditions, "%"+req.OptionContent+"%") |
|||
} |
|||
if req.Username != "" { |
|||
query += " AND u.username LIKE ?" |
|||
conditions = append(conditions, "%"+req.Username+"%") |
|||
} |
|||
if req.Account != "" { |
|||
query += " AND u.account LIKE ?" |
|||
conditions = append(conditions, "%"+req.Account+"%") |
|||
} |
|||
if req.Area != "" { |
|||
query += " AND u.area LIKE ?" |
|||
conditions = append(conditions, "%"+req.Area+"%") |
|||
} |
|||
query += " ORDER BY uv.create_time DESC " |
|||
var voteList []*v1.GetVoteDetailRes |
|||
err = g.DB().Ctx(ctx). |
|||
Raw(query, conditions...). |
|||
Scan(&voteList) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
res = &v1.GetExportFileRes{ |
|||
VoteList: voteList, |
|||
} |
|||
buf, err := json.Marshal(res) |
|||
if err == nil { |
|||
ttl := utils.GetCacheTTL() |
|||
_ = redis.SetEX(ctx, cacheKey, string(buf), int64(ttl)) |
|||
} |
|||
return res, err |
|||
} |
@ -0,0 +1,13 @@ |
|||
package utils |
|||
|
|||
import ( |
|||
"math/rand" |
|||
"practice_ArticleVote_Go/internal/consts" |
|||
"time" |
|||
) |
|||
|
|||
func GetCacheTTL() time.Duration { |
|||
baseTTL := consts.BASE_TTL |
|||
randomNumber := rand.Intn(consts.RANDOM_RANGE) |
|||
return time.Duration(baseTTL+randomNumber) * time.Second |
|||
} |
@ -0,0 +1,45 @@ |
|||
package utils |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"github.com/gogf/gf/v2/frame/g" |
|||
"github.com/gogf/gf/v2/util/gconv" |
|||
) |
|||
|
|||
func DeleteKeys(ctx context.Context, pattern string) (int, error) { |
|||
redis := g.Redis() |
|||
var ( |
|||
cursor uint64 |
|||
totalKeys int |
|||
batchSize = 100 |
|||
) |
|||
g.Log().Debugf(ctx, "开始删除匹配模式 %q 的缓存键", pattern) |
|||
for { |
|||
result, err := redis.Do(ctx, "SCAN", cursor, "MATCH", pattern, "COUNT", batchSize) |
|||
if err != nil { |
|||
return totalKeys, fmt.Errorf("SCAN 失败: %v", err) |
|||
} |
|||
arr := result.Array() |
|||
fmt.Println("arr:", arr) |
|||
cursor = gconv.Uint64(arr[0]) |
|||
rawKeys := gconv.Strings(arr[1]) |
|||
var keys []string |
|||
for _, k := range rawKeys { |
|||
keys = append(keys, k) |
|||
} |
|||
fmt.Println("keys:", keys) |
|||
if len(keys) > 0 { |
|||
deleted, err := redis.Del(ctx, keys...) |
|||
if err != nil { |
|||
g.Log().Warningf(ctx, "缓存删除异常: %v", err) |
|||
} |
|||
totalKeys += int(deleted) |
|||
g.Log().Debugf(ctx, "本次删除 %d/%d 个键", deleted, len(keys)) |
|||
} |
|||
if cursor == 0 { |
|||
break |
|||
} |
|||
} |
|||
return totalKeys, nil |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue