Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
yubinCloud
fairy-wiki
提交
8a1ff3b3
F
fairy-wiki
项目概览
yubinCloud
/
fairy-wiki
通知
4
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
fairy-wiki
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8a1ff3b3
编写于
4月 30, 2021
作者:
yubinCloud
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'study' into main
上级
4c775c26
520f8b41
变更
22
展开全部
隐藏空白更改
内联
并排
Showing
22 changed file
with
1605 addition
and
10 deletion
+1605
-10
README.md
README.md
+58
-2
sql-scripts/create_tables.sql
sql-scripts/create_tables.sql
+15
-1
src/main/java/io/github/yubincloud/fairywiki/config/SpringMvcConfig.java
...o/github/yubincloud/fairywiki/config/SpringMvcConfig.java
+2
-1
src/main/java/io/github/yubincloud/fairywiki/controller/EbookSnapshotController.java
...incloud/fairywiki/controller/EbookSnapshotController.java
+38
-0
src/main/java/io/github/yubincloud/fairywiki/domain/EbookSnapshot.java
.../io/github/yubincloud/fairywiki/domain/EbookSnapshot.java
+92
-0
src/main/java/io/github/yubincloud/fairywiki/domain/EbookSnapshotExample.java
...hub/yubincloud/fairywiki/domain/EbookSnapshotExample.java
+647
-0
src/main/java/io/github/yubincloud/fairywiki/dto/resp/StatisticRespDto.java
...ithub/yubincloud/fairywiki/dto/resp/StatisticRespDto.java
+21
-0
src/main/java/io/github/yubincloud/fairywiki/job/EbookSnapshotJob.java
.../io/github/yubincloud/fairywiki/job/EbookSnapshotJob.java
+38
-0
src/main/java/io/github/yubincloud/fairywiki/mapper/EbookSnapshotMapper.java
...thub/yubincloud/fairywiki/mapper/EbookSnapshotMapper.java
+30
-0
src/main/java/io/github/yubincloud/fairywiki/mapper/EbookSnapshotMapperCustom.java
...ubincloud/fairywiki/mapper/EbookSnapshotMapperCustom.java
+13
-0
src/main/java/io/github/yubincloud/fairywiki/service/EbookSnapshotService.java
...ub/yubincloud/fairywiki/service/EbookSnapshotService.java
+42
-0
src/main/resources/generator/generator-config.xml
src/main/resources/generator/generator-config.xml
+1
-1
src/main/resources/mapper/EbookSnapshotMapper.xml
src/main/resources/mapper/EbookSnapshotMapper.xml
+243
-0
src/main/resources/mapper/EbookSnapshotMapperCustom.xml
src/main/resources/mapper/EbookSnapshotMapperCustom.xml
+73
-0
web/package.json
web/package.json
+1
-0
web/public/index.html
web/public/index.html
+13
-1
web/src/assets/fairy-wiki.png
web/src/assets/fairy-wiki.png
+0
-0
web/src/components/the-header.vue
web/src/components/the-header.vue
+13
-1
web/src/components/the-welcome.vue
web/src/components/the-welcome.vue
+254
-0
web/src/views/admin/admin-doc.vue
web/src/views/admin/admin-doc.vue
+2
-2
web/src/views/doc.vue
web/src/views/doc.vue
+6
-0
web/src/views/home.vue
web/src/views/home.vue
+3
-1
未找到文件。
README.md
浏览文件 @
8a1ff3b3
#
工程简介
#
Fairy Wiki —— 知识库系统
# 延伸阅读
![](
https://img.shields.io/badge/license-MIT-000000.svg
)
![](
https://img.shields.io/badge/language-Java-orange.svg
)
![](
https://img.shields.io/badge/language-TypeScript-green.svg
)
![
Fairy2_small
](
https://gitee.com/yubinCloud/my-imgs-repo/raw/main/img/Fairy2_small.png
)
可以在云端存储电子书、文档的知识库 Wiki 系统,一个由
**Spring Boot**
+
**Vue3**
搭建的全栈项目:
+
前端 Vue CLI & Ant Design Vue 项目搭建
+
后端 Spring Boot 搭建
## 界面设计
![
FairyWikiDemo
](
https://gitee.com/yubinCloud/my-imgs-repo/raw/main/img/FairyWikiDemo.jpg
)
+
用户管理
+
电子书管理
+
文档管理
+
分类管理
+
富文本框的集成
+
图形统计报表展示
+
....
## 关键技术点
+
**axios**
解决前后端分离架构的通信问题
+
**AOP**
日志记录
+
**RocketMQ、WebSocket**
异步化实现消息通知
+
**ECharts**
用于数据统计展示
+
定时任务设计
+
**Redis**
存储用户 token 和登陆校验
+
**Ant Design for Vue**
用于构建前端界面
+
多环境配置文件分别用于开发和生产
+
**统一异常处理**
+
**拦截器**
、
**过滤器**
+
......
## 启动方式
需要分别启动前端和后端
+
后端启动方式:
+
使用 IDEA 打开后,安装
**lombok**
插件
+
启动 Redis
+
启动 RocketMQ
+
以 Maven 方式运行该 Spring Boot 项目
+
前端启动方式:
+
在 /web 子目录下,运行一下命令:
```
bash
$
npm
install
...
$
npm start serve-dev
...
```
\ No newline at end of file
sql-scripts/create_tables.sql
浏览文件 @
8a1ff3b3
...
...
@@ -86,4 +86,18 @@ create table `user` (
unique
key
`login_name_unique`
(
`login_name`
)
)
engine
=
innodb
default
charset
=
utf8mb4
comment
=
'用户'
;
insert
into
`user`
(
id
,
`login_name`
,
`name`
,
`password`
)
values
(
1
,
'test'
,
'测试'
,
'e70e2222a9d67c4f2eae107533359aa4'
);
\ No newline at end of file
insert
into
`user`
(
id
,
`login_name`
,
`name`
,
`password`
)
values
(
1
,
'test'
,
'测试'
,
'e70e2222a9d67c4f2eae107533359aa4'
);
-- 电子书快照表
drop
table
if
exists
`ebook_snapshot`
;
create
table
`ebook_snapshot`
(
`id`
bigint
auto_increment
not
null
comment
'id'
,
`ebook_id`
bigint
not
null
default
0
comment
'电子书id'
,
`date`
date
not
null
comment
'快照日期'
,
`view_count`
int
not
null
default
0
comment
'阅读数'
,
`vote_count`
int
not
null
default
0
comment
'点赞数'
,
`view_increase`
int
not
null
default
0
comment
'阅读增长'
,
`vote_increase`
int
not
null
default
0
comment
'点赞增长'
,
primary
key
(
`id`
),
unique
key
`ebook_id_date_unique`
(
`ebook_id`
,
`date`
)
)
engine
=
innodb
default
charset
=
utf8mb4
comment
=
'电子书快照表'
;
\ No newline at end of file
src/main/java/io/github/yubincloud/fairywiki/config/SpringMvcConfig.java
浏览文件 @
8a1ff3b3
...
...
@@ -24,7 +24,8 @@ public class SpringMvcConfig implements WebMvcConfigurer {
"/ebook/query"
,
"/doc/all/**"
,
"/doc/vote/**"
,
"/doc/read-content/**"
"/doc/read-content/**"
,
"/ebook-snapshot/**"
);
}
}
src/main/java/io/github/yubincloud/fairywiki/controller/EbookSnapshotController.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.controller
;
import
io.github.yubincloud.fairywiki.dto.resp.ErrorCode
;
import
io.github.yubincloud.fairywiki.dto.resp.RestfulModel
;
import
io.github.yubincloud.fairywiki.dto.resp.StatisticRespDto
;
import
io.github.yubincloud.fairywiki.service.EbookSnapshotService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.annotation.Resource
;
import
java.util.List
;
@RestController
@RequestMapping
(
"/ebook-snapshot"
)
@Api
(
"电子书快照管理"
)
public
class
EbookSnapshotController
{
@Resource
private
EbookSnapshotService
ebookSnapshotService
;
@GetMapping
(
"/get-statistic"
)
@ApiOperation
(
value
=
"从电子书快照中昨天和今天的获取统计数据"
)
public
RestfulModel
<
List
<
StatisticRespDto
>>
getStatistic
()
{
List
<
StatisticRespDto
>
statisticRespDtoList
=
ebookSnapshotService
.
getStatistic
();
return
new
RestfulModel
<>(
ErrorCode
.
SUCCESS
,
""
,
statisticRespDtoList
);
}
@GetMapping
(
"/get-30-statistic"
)
@ApiOperation
(
value
=
"从电子书快照中获取近30天的统计数据"
)
public
RestfulModel
<
List
<
StatisticRespDto
>>
get30DayStatistic
()
{
List
<
StatisticRespDto
>
statisticRespDtoList
=
ebookSnapshotService
.
get30DayStatistic
();
return
new
RestfulModel
<>(
ErrorCode
.
SUCCESS
,
""
,
statisticRespDtoList
);
}
}
src/main/java/io/github/yubincloud/fairywiki/domain/EbookSnapshot.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.domain
;
import
java.util.Date
;
public
class
EbookSnapshot
{
private
Long
id
;
private
Long
ebookId
;
private
Date
date
;
private
Integer
viewCount
;
private
Integer
voteCount
;
private
Integer
viewIncrease
;
private
Integer
voteIncrease
;
public
Long
getId
()
{
return
id
;
}
public
void
setId
(
Long
id
)
{
this
.
id
=
id
;
}
public
Long
getEbookId
()
{
return
ebookId
;
}
public
void
setEbookId
(
Long
ebookId
)
{
this
.
ebookId
=
ebookId
;
}
public
Date
getDate
()
{
return
date
;
}
public
void
setDate
(
Date
date
)
{
this
.
date
=
date
;
}
public
Integer
getViewCount
()
{
return
viewCount
;
}
public
void
setViewCount
(
Integer
viewCount
)
{
this
.
viewCount
=
viewCount
;
}
public
Integer
getVoteCount
()
{
return
voteCount
;
}
public
void
setVoteCount
(
Integer
voteCount
)
{
this
.
voteCount
=
voteCount
;
}
public
Integer
getViewIncrease
()
{
return
viewIncrease
;
}
public
void
setViewIncrease
(
Integer
viewIncrease
)
{
this
.
viewIncrease
=
viewIncrease
;
}
public
Integer
getVoteIncrease
()
{
return
voteIncrease
;
}
public
void
setVoteIncrease
(
Integer
voteIncrease
)
{
this
.
voteIncrease
=
voteIncrease
;
}
@Override
public
String
toString
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
getClass
().
getSimpleName
());
sb
.
append
(
" ["
);
sb
.
append
(
"Hash = "
).
append
(
hashCode
());
sb
.
append
(
", id="
).
append
(
id
);
sb
.
append
(
", ebookId="
).
append
(
ebookId
);
sb
.
append
(
", date="
).
append
(
date
);
sb
.
append
(
", viewCount="
).
append
(
viewCount
);
sb
.
append
(
", voteCount="
).
append
(
voteCount
);
sb
.
append
(
", viewIncrease="
).
append
(
viewIncrease
);
sb
.
append
(
", voteIncrease="
).
append
(
voteIncrease
);
sb
.
append
(
"]"
);
return
sb
.
toString
();
}
}
\ No newline at end of file
src/main/java/io/github/yubincloud/fairywiki/domain/EbookSnapshotExample.java
0 → 100644
浏览文件 @
8a1ff3b3
此差异已折叠。
点击以展开。
src/main/java/io/github/yubincloud/fairywiki/dto/resp/StatisticRespDto.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.dto.resp
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.Data
;
import
java.util.Date
;
@Data
public
class
StatisticRespDto
{
@JsonFormat
(
pattern
=
"MM-dd"
,
timezone
=
"GMT+8"
)
private
Date
date
;
private
int
viewCount
;
private
int
voteCount
;
private
int
viewIncrease
;
private
int
voteIncrease
;
}
src/main/java/io/github/yubincloud/fairywiki/job/EbookSnapshotJob.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.job
;
import
io.github.yubincloud.fairywiki.service.EbookSnapshotService
;
import
io.github.yubincloud.fairywiki.utils.SnowFlake
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.slf4j.MDC
;
import
org.springframework.scheduling.annotation.Scheduled
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
@Component
public
class
EbookSnapshotJob
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
EbookSnapshotJob
.
class
);
@Resource
private
EbookSnapshotService
ebookSnapshotService
;
@Resource
private
SnowFlake
snowFlake
;
/**
* 自定义cron表达式跑批
* 只有等上一次执行完成,下一次才会在下一个时间点执行,错过就错过
*/
@Scheduled
(
cron
=
"0/59 0 0-12 * * ? "
)
public
void
doSnapshot
()
{
// 增加日志流水号
MDC
.
put
(
"LOG_ID"
,
String
.
valueOf
(
snowFlake
.
nextId
()));
LOG
.
info
(
"生成今日电子书快照开始"
);
long
start
=
System
.
currentTimeMillis
();
ebookSnapshotService
.
genSnapshots
();
LOG
.
info
(
"生成今日电子书快照结束,耗时:{}毫秒"
,
System
.
currentTimeMillis
()
-
start
);
}
}
src/main/java/io/github/yubincloud/fairywiki/mapper/EbookSnapshotMapper.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.mapper
;
import
io.github.yubincloud.fairywiki.domain.EbookSnapshot
;
import
io.github.yubincloud.fairywiki.domain.EbookSnapshotExample
;
import
java.util.List
;
import
org.apache.ibatis.annotations.Param
;
public
interface
EbookSnapshotMapper
{
long
countByExample
(
EbookSnapshotExample
example
);
int
deleteByExample
(
EbookSnapshotExample
example
);
int
deleteByPrimaryKey
(
Long
id
);
int
insert
(
EbookSnapshot
record
);
int
insertSelective
(
EbookSnapshot
record
);
List
<
EbookSnapshot
>
selectByExample
(
EbookSnapshotExample
example
);
EbookSnapshot
selectByPrimaryKey
(
Long
id
);
int
updateByExampleSelective
(
@Param
(
"record"
)
EbookSnapshot
record
,
@Param
(
"example"
)
EbookSnapshotExample
example
);
int
updateByExample
(
@Param
(
"record"
)
EbookSnapshot
record
,
@Param
(
"example"
)
EbookSnapshotExample
example
);
int
updateByPrimaryKeySelective
(
EbookSnapshot
record
);
int
updateByPrimaryKey
(
EbookSnapshot
record
);
}
\ No newline at end of file
src/main/java/io/github/yubincloud/fairywiki/mapper/EbookSnapshotMapperCustom.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.mapper
;
import
io.github.yubincloud.fairywiki.dto.resp.StatisticRespDto
;
import
java.util.List
;
public
interface
EbookSnapshotMapperCustom
{
void
genSnapshot
();
List
<
StatisticRespDto
>
getStatistic
();
List
<
StatisticRespDto
>
get30DayStatistic
();
}
src/main/java/io/github/yubincloud/fairywiki/service/EbookSnapshotService.java
0 → 100644
浏览文件 @
8a1ff3b3
package
io.github.yubincloud.fairywiki.service
;
import
io.github.yubincloud.fairywiki.dto.resp.StatisticRespDto
;
import
io.github.yubincloud.fairywiki.mapper.EbookSnapshotMapperCustom
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.util.List
;
@Service
public
class
EbookSnapshotService
{
@Resource
private
EbookSnapshotMapperCustom
ebookSnapshotMapperCustom
;
public
void
genSnapshots
()
{
ebookSnapshotMapperCustom
.
genSnapshot
();
}
/**
* 获取首页数值数据:总阅读数、总点赞数、今日阅读数、今日点赞数、今日预计阅读数、今日预计阅读增长
*/
public
List
<
StatisticRespDto
>
getStatistic
()
{
List
<
StatisticRespDto
>
statisticDataList
=
ebookSnapshotMapperCustom
.
getStatistic
();
if
(
statisticDataList
.
size
()
<
2
)
{
if
(
statisticDataList
.
isEmpty
())
{
statisticDataList
.
add
(
null
);
statisticDataList
.
add
(
null
);
}
else
{
statisticDataList
.
add
(
0
,
null
);
}
}
return
statisticDataList
;
}
/**
* 30天数值统计
*/
public
List
<
StatisticRespDto
>
get30DayStatistic
()
{
return
ebookSnapshotMapperCustom
.
get30DayStatistic
();
}
}
src/main/resources/generator/generator-config.xml
浏览文件 @
8a1ff3b3
...
...
@@ -43,6 +43,6 @@
targetPackage=
"io.github.yubincloud.fairywiki.mapper"
type=
"XMLMAPPER"
/>
<table
tableName=
"
user"
domainObjectName=
"User
"
/>
<table
tableName=
"
ebook_snapshot
"
/>
</context>
</generatorConfiguration>
src/main/resources/mapper/EbookSnapshotMapper.xml
0 → 100644
浏览文件 @
8a1ff3b3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"io.github.yubincloud.fairywiki.mapper.EbookSnapshotMapper"
>
<resultMap
id=
"BaseResultMap"
type=
"io.github.yubincloud.fairywiki.domain.EbookSnapshot"
>
<id
column=
"id"
jdbcType=
"BIGINT"
property=
"id"
/>
<result
column=
"ebook_id"
jdbcType=
"BIGINT"
property=
"ebookId"
/>
<result
column=
"date"
jdbcType=
"DATE"
property=
"date"
/>
<result
column=
"view_count"
jdbcType=
"INTEGER"
property=
"viewCount"
/>
<result
column=
"vote_count"
jdbcType=
"INTEGER"
property=
"voteCount"
/>
<result
column=
"view_increase"
jdbcType=
"INTEGER"
property=
"viewIncrease"
/>
<result
column=
"vote_increase"
jdbcType=
"INTEGER"
property=
"voteIncrease"
/>
</resultMap>
<sql
id=
"Example_Where_Clause"
>
<where>
<foreach
collection=
"oredCriteria"
item=
"criteria"
separator=
"or"
>
<if
test=
"criteria.valid"
>
<trim
prefix=
"("
prefixOverrides=
"and"
suffix=
")"
>
<foreach
collection=
"criteria.criteria"
item=
"criterion"
>
<choose>
<when
test=
"criterion.noValue"
>
and ${criterion.condition}
</when>
<when
test=
"criterion.singleValue"
>
and ${criterion.condition} #{criterion.value}
</when>
<when
test=
"criterion.betweenValue"
>
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when
test=
"criterion.listValue"
>
and ${criterion.condition}
<foreach
close=
")"
collection=
"criterion.value"
item=
"listItem"
open=
"("
separator=
","
>
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql
id=
"Update_By_Example_Where_Clause"
>
<where>
<foreach
collection=
"example.oredCriteria"
item=
"criteria"
separator=
"or"
>
<if
test=
"criteria.valid"
>
<trim
prefix=
"("
prefixOverrides=
"and"
suffix=
")"
>
<foreach
collection=
"criteria.criteria"
item=
"criterion"
>
<choose>
<when
test=
"criterion.noValue"
>
and ${criterion.condition}
</when>
<when
test=
"criterion.singleValue"
>
and ${criterion.condition} #{criterion.value}
</when>
<when
test=
"criterion.betweenValue"
>
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when
test=
"criterion.listValue"
>
and ${criterion.condition}
<foreach
close=
")"
collection=
"criterion.value"
item=
"listItem"
open=
"("
separator=
","
>
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql
id=
"Base_Column_List"
>
id, ebook_id, `date`, view_count, vote_count, view_increase, vote_increase
</sql>
<select
id=
"selectByExample"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshotExample"
resultMap=
"BaseResultMap"
>
select
<if
test=
"distinct"
>
distinct
</if>
<include
refid=
"Base_Column_List"
/>
from ebook_snapshot
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
<if
test=
"orderByClause != null"
>
order by ${orderByClause}
</if>
</select>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.Long"
resultMap=
"BaseResultMap"
>
select
<include
refid=
"Base_Column_List"
/>
from ebook_snapshot
where id = #{id,jdbcType=BIGINT}
</select>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.Long"
>
delete from ebook_snapshot
where id = #{id,jdbcType=BIGINT}
</delete>
<delete
id=
"deleteByExample"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshotExample"
>
delete from ebook_snapshot
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
</delete>
<insert
id=
"insert"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshot"
>
insert into ebook_snapshot (id, ebook_id, `date`,
view_count, vote_count, view_increase,
vote_increase)
values (#{id,jdbcType=BIGINT}, #{ebookId,jdbcType=BIGINT}, #{date,jdbcType=DATE},
#{viewCount,jdbcType=INTEGER}, #{voteCount,jdbcType=INTEGER}, #{viewIncrease,jdbcType=INTEGER},
#{voteIncrease,jdbcType=INTEGER})
</insert>
<insert
id=
"insertSelective"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshot"
>
insert into ebook_snapshot
<trim
prefix=
"("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
id,
</if>
<if
test=
"ebookId != null"
>
ebook_id,
</if>
<if
test=
"date != null"
>
`date`,
</if>
<if
test=
"viewCount != null"
>
view_count,
</if>
<if
test=
"voteCount != null"
>
vote_count,
</if>
<if
test=
"viewIncrease != null"
>
view_increase,
</if>
<if
test=
"voteIncrease != null"
>
vote_increase,
</if>
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id,jdbcType=BIGINT},
</if>
<if
test=
"ebookId != null"
>
#{ebookId,jdbcType=BIGINT},
</if>
<if
test=
"date != null"
>
#{date,jdbcType=DATE},
</if>
<if
test=
"viewCount != null"
>
#{viewCount,jdbcType=INTEGER},
</if>
<if
test=
"voteCount != null"
>
#{voteCount,jdbcType=INTEGER},
</if>
<if
test=
"viewIncrease != null"
>
#{viewIncrease,jdbcType=INTEGER},
</if>
<if
test=
"voteIncrease != null"
>
#{voteIncrease,jdbcType=INTEGER},
</if>
</trim>
</insert>
<select
id=
"countByExample"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshotExample"
resultType=
"java.lang.Long"
>
select count(*) from ebook_snapshot
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
</select>
<update
id=
"updateByExampleSelective"
parameterType=
"map"
>
update ebook_snapshot
<set>
<if
test=
"record.id != null"
>
id = #{record.id,jdbcType=BIGINT},
</if>
<if
test=
"record.ebookId != null"
>
ebook_id = #{record.ebookId,jdbcType=BIGINT},
</if>
<if
test=
"record.date != null"
>
`date` = #{record.date,jdbcType=DATE},
</if>
<if
test=
"record.viewCount != null"
>
view_count = #{record.viewCount,jdbcType=INTEGER},
</if>
<if
test=
"record.voteCount != null"
>
vote_count = #{record.voteCount,jdbcType=INTEGER},
</if>
<if
test=
"record.viewIncrease != null"
>
view_increase = #{record.viewIncrease,jdbcType=INTEGER},
</if>
<if
test=
"record.voteIncrease != null"
>
vote_increase = #{record.voteIncrease,jdbcType=INTEGER},
</if>
</set>
<if
test=
"_parameter != null"
>
<include
refid=
"Update_By_Example_Where_Clause"
/>
</if>
</update>
<update
id=
"updateByExample"
parameterType=
"map"
>
update ebook_snapshot
set id = #{record.id,jdbcType=BIGINT},
ebook_id = #{record.ebookId,jdbcType=BIGINT},
`date` = #{record.date,jdbcType=DATE},
view_count = #{record.viewCount,jdbcType=INTEGER},
vote_count = #{record.voteCount,jdbcType=INTEGER},
view_increase = #{record.viewIncrease,jdbcType=INTEGER},
vote_increase = #{record.voteIncrease,jdbcType=INTEGER}
<if
test=
"_parameter != null"
>
<include
refid=
"Update_By_Example_Where_Clause"
/>
</if>
</update>
<update
id=
"updateByPrimaryKeySelective"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshot"
>
update ebook_snapshot
<set>
<if
test=
"ebookId != null"
>
ebook_id = #{ebookId,jdbcType=BIGINT},
</if>
<if
test=
"date != null"
>
`date` = #{date,jdbcType=DATE},
</if>
<if
test=
"viewCount != null"
>
view_count = #{viewCount,jdbcType=INTEGER},
</if>
<if
test=
"voteCount != null"
>
vote_count = #{voteCount,jdbcType=INTEGER},
</if>
<if
test=
"viewIncrease != null"
>
view_increase = #{viewIncrease,jdbcType=INTEGER},
</if>
<if
test=
"voteIncrease != null"
>
vote_increase = #{voteIncrease,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update
id=
"updateByPrimaryKey"
parameterType=
"io.github.yubincloud.fairywiki.domain.EbookSnapshot"
>
update ebook_snapshot
set ebook_id = #{ebookId,jdbcType=BIGINT},
`date` = #{date,jdbcType=DATE},
view_count = #{viewCount,jdbcType=INTEGER},
vote_count = #{voteCount,jdbcType=INTEGER},
view_increase = #{viewIncrease,jdbcType=INTEGER},
vote_increase = #{voteIncrease,jdbcType=INTEGER}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
\ No newline at end of file
src/main/resources/mapper/EbookSnapshotMapperCustom.xml
0 → 100644
浏览文件 @
8a1ff3b3
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper
namespace=
"io.github.yubincloud.fairywiki.mapper.EbookSnapshotMapperCustom"
>
<!--
# 方案一(ID不连续):
# 删除今天的数据
# 为所有的电子书生成一条今天的记录
# 更新总阅读数、总点赞数
# 更新今日阅读数、今日点赞数
# 方案二(ID连续):
# 为所有的电子书生成一条今天的记录,如果还没有
# 更新总阅读数、总点赞数
# 更新今日阅读数、今日点赞数
-->
<update
id=
"genSnapshot"
>
INSERT INTO ebook_snapshot(ebook_id, `date`, view_count, vote_count, view_increase, vote_increase)
SELECT t1.id, curdate(), 0, 0, 0, 0
FROM ebook t1
WHERE NOT EXISTS(SELECT 1
FROM ebook_snapshot t2
WHERE t1.id = t2.ebook_id
AND t2.`date` = curdate());
UPDATE ebook_snapshot t1, ebook t2
SET t1.view_count = t2.view_count,
t1.vote_count = t2.vote_count
WHERE t1.`date` = curdate()
AND t1.ebook_id = t2.id;
UPDATE ebook_snapshot t1 LEFT JOIN (SELECT ebook_id, view_count, vote_count
FROM ebook_snapshot
WHERE `date` = date_sub(curdate(), INTERVAL 1 DAY)) t2
ON t1.ebook_id = t2.ebook_id
SET t1.view_increase = (t1.view_count - ifnull(t2.view_count, 0)),
t1.vote_increase = (t1.vote_count - ifnull(t2.vote_count, 0))
WHERE t1.`date` = curdate();
</update>
<select
id=
"getStatistic"
resultType=
"io.github.yubincloud.fairywiki.dto.resp.StatisticRespDto"
>
SELECT
t1.`date` AS `date`,
SUM(t1.view_count) AS viewCount,
SUM(t1.vote_count) AS voteCount,
SUM(t1.view_increase) AS viewIncrease,
SUM(t1.vote_increase) AS voteIncrease
FROM
ebook_snapshot t1
WHERE
t1.`date` >= date_sub(curdate(), INTERVAL 1 DAY)
GROUP BY
t1.`date`
ORDER BY
t1.`date` ASC;
</select>
<select
id=
"get30DayStatistic"
resultType=
"io.github.yubincloud.fairywiki.dto.resp.StatisticRespDto"
>
SELECT
t1.`date` AS `date`,
SUM(t1.view_count) AS viewCount,
SUM(t1.vote_count) AS voteCount
FROM
ebook_snapshot t1
WHERE
t1.`date` >= date_sub(curdate(), INTERVAL 30 DAY)
GROUP BY
t1.`date`
ORDER BY
t1.`date` ASC;
</select>
</mapper>
\ No newline at end of file
web/package.json
浏览文件 @
8a1ff3b3
...
...
@@ -13,6 +13,7 @@
"@ant-design/icons-vue"
:
"^6.0.1"
,
"ant-design-vue"
:
"^2.0.0-rc.3"
,
"axios"
:
"^0.21.0"
,
"echarts"
:
"^5.1.1"
,
"vue"
:
"^3.0.0"
,
"vue-router"
:
"^4.0.0-0"
,
"vuex"
:
"^4.0.0-0"
,
...
...
web/public/index.html
浏览文件 @
8a1ff3b3
...
...
@@ -13,7 +13,19 @@
<noscript>
<strong>
We're sorry but
<
%=
htmlWebpackPlugin.options.title
%
>
doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div
id=
"app"
></div>
<div
id=
"app"
>
<div
style=
"width: 400px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -200px;font-family: YouYuan;
color: rgba(0, 0, 0, 1) !important;
font-size: 20px !important;
font-weight: 400;"
>
首次加载会较慢,正在进入,请等待...
</div>
</div>
<!-- built files will be auto injected -->
</body>
</html>
web/src/assets/fairy-wiki.png
0 → 100644
浏览文件 @
8a1ff3b3
4.4 KB
web/src/components/the-header.vue
浏览文件 @
8a1ff3b3
<
template
>
<a-layout-header
class=
"header"
>
<div
class=
"logo"
/>
<div
class=
"logo"
>
<img
alt=
"Fairy Wiki"
src=
"../assets/fairy-wiki.png"
>
</div>
<a-menu
theme=
"dark"
mode=
"horizontal"
...
...
@@ -131,6 +133,16 @@ export default defineComponent({
</
script
>
<
style
>
.logo
{
width
:
120px
;
height
:
31px
;
/*background: rgba(255, 255, 255, 0.2);*/
/*margin: 16px 28px 16px 0;*/
float
:
left
;
color
:
white
;
font-size
:
18px
;
}
.login-menu
{
float
:
right
;
color
:
white
;
...
...
web/src/components/the-welcome.vue
0 → 100644
浏览文件 @
8a1ff3b3
<
template
>
<div>
<a-row>
<a-col
:span=
"24"
>
<a-card>
<a-row>
<a-col
:span=
"8"
>
<a-statistic
title=
"总阅读量"
:value=
"statistic.viewCount"
>
<template
#suffix
>
<UserOutlined
/>
</
template
>
</a-statistic>
</a-col>
<a-col
:span=
"8"
>
<a-statistic
title=
"总点赞量"
:value=
"statistic.voteCount"
>
<
template
#suffix
>
<like-outlined
/>
</
template
>
</a-statistic>
</a-col>
<a-col
:span=
"8"
>
<a-statistic
title=
"点赞率"
:value=
"statistic.voteCount / statistic.viewCount * 100"
:precision=
"2"
suffix=
"%"
:value-style=
"{ color: '#cf1322' }"
>
<
template
#suffix
>
<like-outlined
/>
</
template
>
</a-statistic>
</a-col>
</a-row>
</a-card>
</a-col>
</a-row>
<br>
<a-row
:gutter=
"16"
>
<a-col
:span=
"12"
>
<a-card>
<a-row>
<a-col
:span=
"12"
>
<a-statistic
title=
"今日阅读"
:value=
"statistic.todayViewCount"
style=
"margin-right: 50px"
>
<
template
#suffix
>
<UserOutlined
/>
</
template
>
</a-statistic>
</a-col>
<a-col
:span=
"12"
>
<a-statistic
title=
"今日点赞"
:value=
"statistic.todayVoteCount"
>
<
template
#suffix
>
<like-outlined
/>
</
template
>
</a-statistic>
</a-col>
</a-row>
</a-card>
</a-col>
<a-col
:span=
"12"
>
<a-card>
<a-row>
<a-col
:span=
"12"
>
<a-statistic
title=
"预计今日阅读"
:value=
"statistic.todayViewIncrease"
:value-style=
"{ color: '#0000ff' }"
>
<
template
#suffix
>
<UserOutlined
/>
</
template
>
</a-statistic>
</a-col>
<a-col
:span=
"12"
>
<a-statistic
title=
"预计今日阅读增长"
:value=
"statistic.todayViewIncreaseRateAbs"
:precision=
"2"
suffix=
"%"
class=
"demo-class"
:value-style=
"statistic.todayViewIncreaseRate < 0 ? { color: '#3f8600' } : { color: '#cf1322' }"
>
<
template
#prefix
>
<arrow-down-outlined
v-if=
"statistic.todayViewIncreaseRate
<
0"
/>
<arrow-up-outlined
v-if=
"statistic.todayViewIncreaseRate >= 0"
/>
</
template
>
</a-statistic>
</a-col>
</a-row>
</a-card>
</a-col>
</a-row>
<br>
<a-row>
<a-col
:span=
"24"
>
<div
id=
"chart"
style=
"width: 100%;height:300px;"
></div>
</a-col>
</a-row>
</div>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
,
ref
,
onMounted
}
from
'
vue
'
import
axios
from
'
axios
'
;
import
*
as
echarts
from
'
echarts/core
'
;
import
{
BarChart
,
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption
,
LineChart
,
LineSeriesOption
}
from
'
echarts/charts
'
;
import
{
TitleComponent
,
// 组件类型的定义后缀都为 ComponentOption
TitleComponentOption
,
GridComponent
,
GridComponentOption
,
TooltipComponent
}
from
'
echarts/components
'
;
import
{
CanvasRenderer
}
from
'
echarts/renderers
'
;
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type
ECOption
=
echarts
.
ComposeOption
<
BarSeriesOption
|
LineSeriesOption
|
TitleComponentOption
|
GridComponentOption
>
;
// 注册必须的组件
echarts
.
use
(
[
TitleComponent
,
TooltipComponent
,
GridComponent
,
BarChart
,
CanvasRenderer
]
);
export
default
defineComponent
({
name
:
'
the-welcome
'
,
setup
()
{
const
statistic
=
ref
();
statistic
.
value
=
{};
/**
* 生成昨天和今天的数据统计表格
*/
const
genTwoDayStatisticTable
=
()
=>
{
axios
.
get
(
'
/ebook-snapshot/get-statistic
'
).
then
((
response
)
=>
{
const
respData
=
response
.
data
;
if
(
respData
.
code
===
0
)
{
const
statisticData
=
respData
.
data
;
statistic
.
value
.
viewCount
=
statisticData
[
1
].
viewCount
;
statistic
.
value
.
voteCount
=
statisticData
[
1
].
voteCount
;
statistic
.
value
.
todayViewCount
=
statisticData
[
1
].
viewIncrease
;
statistic
.
value
.
todayVoteCount
=
statisticData
[
1
].
voteIncrease
;
// 按分钟计算当前时间点,占一天的百分比
const
now
=
new
Date
();
const
nowRate
=
(
now
.
getHours
()
*
60
+
now
.
getMinutes
())
/
(
60
*
24
);
// console.log(nowRate)
statistic
.
value
.
todayViewIncrease
=
parseInt
(
String
(
statisticData
[
1
].
viewIncrease
/
nowRate
));
// todayViewIncreaseRate:今日预计增长率
statistic
.
value
.
todayViewIncreaseRate
=
(
statistic
.
value
.
todayViewIncrease
-
statisticData
[
0
].
viewIncrease
)
/
statisticData
[
0
].
viewIncrease
*
100
;
statistic
.
value
.
todayViewIncreaseRateAbs
=
Math
.
abs
(
statistic
.
value
.
todayViewIncreaseRate
);
}
});
};
const
init30DayEcharts
=
(
list
:
any
)
=>
{
// 基于准备好的dom,初始化echarts实例
const
statisticChart
=
echarts
.
init
(
document
.
getElementById
(
'
chart
'
)
as
HTMLCanvasElement
);
const
xAxis
=
[];
const
seriesView
=
[];
const
seriesVote
=
[];
for
(
let
i
=
0
;
i
<
list
.
length
;
i
++
)
{
const
record
=
list
[
i
];
xAxis
.
push
(
record
.
date
);
seriesView
.
push
(
record
.
viewIncrease
);
seriesVote
.
push
(
record
.
voteIncrease
);
}
// 指定图表的配置项和数据
const
option
=
{
title
:
{
text
:
'
30天趋势图
'
},
tooltip
:
{
trigger
:
'
axis
'
},
legend
:
{
data
:
[
'
总阅读量
'
,
'
总点赞量
'
]
},
grid
:
{
left
:
'
1%
'
,
right
:
'
3%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
},
toolbox
:
{
feature
:
{
saveAsImage
:
{}
}
},
xAxis
:
{
type
:
'
category
'
,
boundaryGap
:
false
,
data
:
xAxis
},
yAxis
:
{
type
:
'
value
'
},
series
:
[
{
name
:
'
总阅读量
'
,
type
:
'
line
'
,
// stack: '总量', 不堆叠
data
:
seriesView
,
smooth
:
true
},
{
name
:
'
总点赞量
'
,
type
:
'
line
'
,
// stack: '总量', 不堆叠
data
:
seriesVote
,
smooth
:
true
}
]
};
// 使用刚指定的配置项和数据显示图表。
statisticChart
.
setOption
(
option
);
};
/**
* 生成近30天的数据统计图
*/
const
gen30DayStatisticChart
=
()
=>
{
axios
.
get
(
'
/ebook-snapshot/get-30-statistic
'
).
then
((
response
)
=>
{
const
respData
=
response
.
data
;
if
(
respData
.
code
===
0
)
{
const
statisticList
=
respData
.
data
;
init30DayEcharts
(
statisticList
);
}
});
};
onMounted
(()
=>
{
genTwoDayStatisticTable
();
gen30DayStatisticChart
();
});
return
{
statistic
}
}
});
</
script
>
\ No newline at end of file
web/src/views/admin/admin-doc.vue
浏览文件 @
8a1ff3b3
...
...
@@ -274,7 +274,7 @@ export default defineComponent({
handleQueryContent
();
// 不能选择当前节点及其所有子孙节点,作为父节点,会使树断开
treeSelectData
.
value
=
Tool
.
copy
(
level1
.
value
);
treeSelectData
.
value
=
Tool
.
copy
(
level1
.
value
)
||
[]
;
setDisable
(
treeSelectData
.
value
,
record
.
id
);
// 为选择树添加一个"无"
...
...
@@ -290,7 +290,7 @@ export default defineComponent({
doc
.
value
=
{
ebookId
:
route
.
query
.
ebookId
};
treeSelectData
.
value
=
Tool
.
copy
(
level1
.
value
);
treeSelectData
.
value
=
Tool
.
copy
(
level1
.
value
)
||
[]
;
// 为选择树添加一个"无"
treeSelectData
.
value
.
unshift
({
id
:
0
,
name
:
'
无
'
});
...
...
web/src/views/doc.vue
浏览文件 @
8a1ff3b3
...
...
@@ -205,4 +205,10 @@ export default defineComponent({
padding
:
15px
;
text-align
:
center
;
}
/* 图片自适应 */
.wangeditor
img
{
max-width
:
100%
;
height
:
auto
;
}
</
style
>
\ No newline at end of file
web/src/views/home.vue
浏览文件 @
8a1ff3b3
...
...
@@ -24,7 +24,7 @@
:style=
"{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
<div
class=
"welcome"
v-show=
"isShowWelcome"
>
<
h1>
欢迎进入 Fairy Wiki
</h1
>
<
the-welcome></the-welcome
>
</div>
<a-list
v-show=
"!isShowWelcome"
item-layout=
"vertical"
size=
"large"
:grid=
"{ gutter: 20, column: 3 }"
:data-source=
"ebooks"
>
...
...
@@ -66,11 +66,13 @@ import { StarOutlined, LikeOutlined, MessageOutlined } from '@ant-design/icons-v
import
{
Tool
}
from
"
@/util/tool
"
;
import
{
message
}
from
"
_ant-design-vue@2.0.0-rc.3@ant-design-vue
"
;
import
{
Category
}
from
"
@/models
"
;
import
TheWelcome
from
"
@/components/the-welcome.vue
"
export
default
defineComponent
({
name
:
'
Home
'
,
components
:
{
TheWelcome
,
StarOutlined
,
LikeOutlined
,
MessageOutlined
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录