Skip to content

Commit 215d1e1

Browse files
committed
新增访问日志查询功能
1、支持指定时间段查询访问日志 2、支持指定时间段内统计点击量及独立IP数
1 parent 80e7c3d commit 215d1e1

File tree

12 files changed

+312
-45
lines changed

12 files changed

+312
-45
lines changed

.dockerignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.vscode
22
docker/container-data/*
33
tmp
4-
build
4+
build
5+
.idea

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ docker/container-data/*
33
!docker/container-data/.keep
44
tmp
55
.DS_Store
6-
build
6+
build
7+
.idea

assets/admin.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,39 @@ $(document).ready(function() {
5353
}
5454
});
5555

56-
$('#form-search-logs').form({
57-
fields: {
58-
url: {rules:[{
59-
type:'empty',
60-
prompt:'链接不能为空'
61-
}]}
56+
$('#logs-search-start-date').calendar({
57+
type:'date',
58+
text: {
59+
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
60+
monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
61+
},
62+
formatter: {
63+
date: function (date, settings) {
64+
if (!date) return '';
65+
var day = date.getDate();
66+
var month = date.getMonth() + 1;
67+
var year = date.getFullYear();
68+
return year + '-' + month + '-' + day;
69+
}
6270
}
6371
});
64-
72+
73+
$('#logs-search-end-date').calendar({
74+
type:'date',
75+
text: {
76+
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
77+
monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
78+
},
79+
formatter: {
80+
date: function (date, settings) {
81+
if (!date) return '';
82+
var day = date.getDate();
83+
var month = date.getMonth() + 1;
84+
var year = date.getFullYear();
85+
return year + '-' + month + '-' + day;
86+
}
87+
}
88+
});
6589

6690
$('#sidebar-menu').sidebar('attach events', '#sidebar-menu-toggler');
6791

controller/admin_controller.go

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func StatsPage(c *gin.Context) {
176176
})
177177
}
178178

179-
func UrlsPage(c *gin.Context) {
179+
func SearchStatsPage(c *gin.Context) {
180180
url := c.DefaultQuery("url", "")
181181
strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
182182
strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
@@ -188,9 +188,9 @@ func UrlsPage(c *gin.Context) {
188188
if err != nil {
189189
size = DEFAULT_PAGE_SIZE
190190
}
191-
urls, err := service.GetPagesShortUrls(strings.TrimSpace(url), page, size)
192-
c.HTML(http.StatusOK, "urls.html", gin.H{
193-
"title": "短链接列表 - ohUrlShortener",
191+
urls, err := service.GetPagedUrlIpCountStats(strings.TrimSpace(url), page, size)
192+
c.HTML(http.StatusOK, "search_stats.html", gin.H{
193+
"title": "查询统计 - ohUrlShortener",
194194
"current_url": c.Request.URL.Path,
195195
"error": err,
196196
"shortUrls": urls,
@@ -203,7 +203,7 @@ func UrlsPage(c *gin.Context) {
203203
})
204204
}
205205

206-
func AccessLogsPage(c *gin.Context) {
206+
func UrlsPage(c *gin.Context) {
207207
url := c.DefaultQuery("url", "")
208208
strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
209209
strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
@@ -215,21 +215,56 @@ func AccessLogsPage(c *gin.Context) {
215215
if err != nil {
216216
size = DEFAULT_PAGE_SIZE
217217
}
218-
logs, err := service.GetPagedAccessLogs(strings.TrimSpace(url), page, size)
219-
c.HTML(http.StatusOK, "access_logs.html", gin.H{
220-
"title": "访问日志查询 - ohUrlShortener",
218+
urls, err := service.GetPagesShortUrls(strings.TrimSpace(url), page, size)
219+
c.HTML(http.StatusOK, "urls.html", gin.H{
220+
"title": "短链接列表 - ohUrlShortener",
221221
"current_url": c.Request.URL.Path,
222222
"error": err,
223-
"logs": logs,
223+
"shortUrls": urls,
224224
"page": page,
225225
"size": size,
226226
"prefix": utils.AppConfig.UrlPrefix,
227227
"first_page": page == 1,
228-
"last_page": len(logs) < size,
228+
"last_page": len(urls) < size,
229229
"url": strings.TrimSpace(url),
230230
})
231231
}
232232

233+
func AccessLogsPage(c *gin.Context) {
234+
url := c.DefaultQuery("url", "")
235+
strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
236+
strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
237+
start := c.DefaultQuery("start", "")
238+
end := c.DefaultQuery("end", "")
239+
page, err := strconv.Atoi(strPage)
240+
if err != nil {
241+
page = DEFAULT_PAGE_NUM
242+
}
243+
size, err := strconv.Atoi(strSize)
244+
if err != nil {
245+
size = DEFAULT_PAGE_SIZE
246+
}
247+
248+
totalCount, distinctIpCount, err := service.GetAccessLogsCount(strings.TrimSpace(url), start, end)
249+
logs, err := service.GetPagedAccessLogs(strings.TrimSpace(url), start, end, page, size)
250+
c.HTML(http.StatusOK, "access_logs.html", gin.H{
251+
"title": "访问日志查询 - ohUrlShortener",
252+
"current_url": c.Request.URL.Path,
253+
"error": err,
254+
"logs": logs,
255+
"page": page,
256+
"size": size,
257+
"prefix": utils.AppConfig.UrlPrefix,
258+
"first_page": page == 1,
259+
"last_page": len(logs) < size,
260+
"url": strings.TrimSpace(url),
261+
"total_count": totalCount,
262+
"unique_ip_count": distinctIpCount,
263+
"start_date": start,
264+
"end_date": end,
265+
})
266+
}
267+
233268
func AccessLogsExport(c *gin.Context) {
234269
url := c.PostForm("url")
235270
logs, err := service.GetAllAccessLogs(strings.TrimSpace(url))

docker/dev.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
version: '3'
2+
services:
3+
4+
postgres:
5+
image: postgres:${PG_VERSION}
6+
container_name: ${PG_CONTAINER_NAME}
7+
hostname: postgres
8+
env_file:
9+
- ../docker/vars.env
10+
environment:
11+
- POSTGRES_USER=${PG_SUPER_USER}
12+
- POSTGRES_PASSWORD=${PG_SUPER_PWD}
13+
- TZ=PRC
14+
- PGTZ=PRC
15+
volumes:
16+
- ../structure.sql:/docker-entrypoint-initdb.d/001.sql
17+
- ../docker/container-data/postgresql:/var/lib/postgresql/data
18+
ports:
19+
- ${PG_LOCAL_PORT}:5432
20+
healthcheck:
21+
test: [ "CMD", "psql", "-U","${PG_SUPER_USER}","-d","oh_url_shortener" ]
22+
timeout: 10s
23+
interval: 3s
24+
retries: 10
25+
networks:
26+
- ohurlshortener
27+
28+
redis:
29+
image: redis:${RD_VERSION}
30+
container_name: ${RD_CONTAINER_NAME}
31+
hostname: redis
32+
env_file:
33+
- ../docker/vars.env
34+
ports:
35+
- ${RD_LOCAL_PORT}:6379
36+
healthcheck:
37+
test: [ "CMD", "redis-cli","-p","6379"]
38+
timeout: 10s
39+
interval: 3s
40+
retries: 10
41+
networks:
42+
- ohurlshortener
43+
44+
networks:
45+
ohurlshortener:
46+
driver: bridge
47+
name: "network_ohurlshortener"
48+
driver_opts:
49+
com.docker.network.enable_ipv6: "true"

docker/vars.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
# ohUrlShortenerAdmin
44
OH_ADMIN_PORT=9092
5-
OH_ADMIN_VERSION=1.3
5+
OH_ADMIN_VERSION=1.4
66
OH_ADMIN_CONTAINER_NAME=ohurlshortener_admin
77

88
# ohUrlShortenerPortal
99
OH_PORTAL_PORT=9091
10-
OH_PORTAL_VERSION=1.3
10+
OH_PORTAL_VERSION=1.4
1111
OH_PORTAL_CONTAINER_NAME=ohurlshortener_portal
1212

1313
# Postgresql Vars

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ func initializeRoute02() (http.Handler, error) {
207207
admin.GET("/dashboard", controller.DashbaordPage)
208208
admin.GET("/urls", controller.UrlsPage)
209209
admin.GET("/stats", controller.StatsPage)
210+
admin.GET("/search_stats", controller.SearchStatsPage)
210211
admin.GET("/access_logs", controller.AccessLogsPage)
211212
admin.POST("/urls/generate", controller.GenerateShortUrl)
212213
admin.POST("/urls/state", controller.ChangeState)

service/access_logs_s.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,22 @@ func StoreAccessLogs() error {
7474
return nil
7575
}
7676

77-
func GetPagedAccessLogs(url string, page int, size int) ([]core.AccessLog, error) {
77+
func GetPagedAccessLogs(url string, start, end string, page, size int) ([]core.AccessLog, error) {
7878
if page < 1 || size < 1 {
7979
return nil, nil
8080
}
81-
allAccessLogs, err := storage.FindAllAccessLogs(url, page, size)
81+
allAccessLogs, err := storage.FindAllAccessLogs(url, start, end, page, size)
8282
if err != nil {
8383
log.Println(err)
8484
return allAccessLogs, utils.RaiseError("内部错误,请联系管理员")
8585
}
8686
return allAccessLogs, nil
8787
}
8888

89+
func GetAccessLogsCount(url string, start, end string) (int, int, error) {
90+
return storage.FindAccessLogsCount(url, start, end)
91+
}
92+
8993
func GetAllAccessLogs(url string) ([]core.AccessLog, error) {
9094
allAccessLogs, err := storage.FindAllAccessLogsByUrl(url)
9195
if err != nil {

storage/access_logs_storage.go

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package storage
1010

1111
import (
12+
"fmt"
1213
"ohurlshortener/core"
1314
"ohurlshortener/utils"
1415
)
@@ -37,26 +38,45 @@ func InsertAccessLogs(logs []core.AccessLog) error {
3738
return DbNamedExec(query, logs)
3839
}
3940

40-
func FindAccessLogsCount(url string) (int, error) {
41-
var rowCount int
42-
query := "SELECT count(l.id) FROM public.access_logs l"
41+
// FindAccessLogsCount
42+
//
43+
// Find Access Logs Count and Unique IP Count
44+
//
45+
// First return value is total_count, Second return value is unique_ip_count ip count
46+
func FindAccessLogsCount(url string, start, end string) (int, int, error) {
47+
type LogsCount struct {
48+
TotalCount int `db:"total_count"`
49+
UniqueIpCount int `db:"unique_ip_count"`
50+
}
51+
query := `SELECT count(l.id) as total_count, count(distinct(l.ip)) as unique_ip_count FROM public.access_logs l WHERE 1=1 `
4352
if !utils.EemptyString(url) {
44-
query = "SELECT count(l.id) FROM public.access_logs l WHERE l.short_url = $1"
45-
err := DbGet(query, &rowCount, url)
46-
return rowCount, err
53+
query += fmt.Sprintf(` AND l.short_url = '%s'`, url)
54+
}
55+
if !utils.EemptyString(start) {
56+
query += fmt.Sprintf(` AND l.access_time >= to_date('%s','YYYY-MM-DD')`, start)
4757
}
48-
return rowCount, DbGet(query, &rowCount)
58+
if !utils.EemptyString(end) {
59+
query += fmt.Sprintf(` AND l.access_time < to_date('%s','YYYY-MM-DD')`, end)
60+
}
61+
var count LogsCount
62+
return count.TotalCount, count.UniqueIpCount, DbGet(query, &count)
4963
}
5064

51-
func FindAllAccessLogs(url string, page int, size int) ([]core.AccessLog, error) {
65+
func FindAllAccessLogs(url string, start, end string, page, size int) ([]core.AccessLog, error) {
5266
found := []core.AccessLog{}
5367
offset := (page - 1) * size
54-
query := "SELECT * FROM public.access_logs l ORDER BY l.id DESC LIMIT $1 OFFSET $2"
68+
query := `SELECT * FROM public.access_logs l WHERE 1=1 `
5569
if !utils.EemptyString(url) {
56-
query := "SELECT * FROM public.access_logs l WHERE l.short_url = $1 ORDER BY l.id DESC LIMIT $2 OFFSET $3"
57-
err := DbSelect(query, &found, url, size, offset)
58-
return found, err
70+
query += fmt.Sprintf(` AND l.short_url = '%s'`, url)
5971
}
72+
if !utils.EemptyString(start) {
73+
query += fmt.Sprintf(` AND l.access_time >= to_date('%s','YYYY-MM-DD')`, start)
74+
}
75+
if !utils.EemptyString(end) {
76+
query += fmt.Sprintf(` AND l.access_time < to_date('%s','YYYY-MM-DD')`, end)
77+
}
78+
79+
query += ` ORDER BY l.id DESC LIMIT $1 OFFSET $2`
6080
return found, DbSelect(query, &found, size, offset)
6181
}
6282

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package storage
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestFindAccessLogsCount(t *testing.T) {
8+
init4Test(t)
9+
10+
type args struct {
11+
url string
12+
start string
13+
end string
14+
}
15+
tests := []struct {
16+
name string
17+
args args
18+
want int
19+
want1 int
20+
wantErr bool
21+
}{
22+
{name: "TestCase 1", args: args{url: "", start: "", end: ""}, want: 18, want1: 7, wantErr: false},
23+
{name: "TestCase 2", args: args{url: "gkT39tb5", start: "", end: ""}, want: 6, want1: 4, wantErr: false},
24+
{name: "TestCase 3", args: args{url: "gkT39tb5", start: "2022-04-20", end: ""}, want: 2, want1: 1, wantErr: false},
25+
{name: "TestCase 3", args: args{url: "gkT39tb5", start: "2022-04-07", end: "2022-04-11"}, want: 3, want1: 3, wantErr: false},
26+
{name: "TestCase 1", args: args{url: "", start: "2022-04-01", end: ""}, want: 18, want1: 7, wantErr: false},
27+
}
28+
for _, tt := range tests {
29+
t.Run(tt.name, func(t *testing.T) {
30+
got, got1, err := FindAccessLogsCount(tt.args.url, tt.args.start, tt.args.end)
31+
if (err != nil) != tt.wantErr {
32+
t.Errorf("FindAccessLogsCount() error = %v, wantErr %v", err, tt.wantErr)
33+
return
34+
}
35+
if got != tt.want {
36+
t.Errorf("FindAccessLogsCount() got = %v, want %v", got, tt.want)
37+
}
38+
if got1 != tt.want1 {
39+
t.Errorf("FindAccessLogsCount() got1 = %v, want %v", got1, tt.want1)
40+
}
41+
})
42+
}
43+
}

0 commit comments

Comments
 (0)