Skip to content

Commit 6d5918a

Browse files
committed
展示量& 前端
1 parent 745c6b6 commit 6d5918a

13 files changed

Lines changed: 715 additions & 105 deletions

File tree

yunyu-server/src/main/java/com/ideaflow/yunyu/module/site/controller/SiteContentController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121
import org.springframework.web.bind.annotation.GetMapping;
2222
import org.springframework.web.bind.annotation.PathVariable;
23+
import org.springframework.web.bind.annotation.PostMapping;
2324
import org.springframework.web.bind.annotation.RequestMapping;
2425
import org.springframework.web.bind.annotation.RestController;
2526

@@ -104,6 +105,19 @@ public ApiResponse<SitePostDetailResponse> getPostDetail(@PathVariable String sl
104105
return ApiResponse.success(siteContentService.getPostDetail(slug));
105106
}
106107

108+
/**
109+
* 上报文章浏览量。
110+
*
111+
* @param id 文章 id
112+
* @return 是否上报成功
113+
*/
114+
@Operation(summary = "上报文章浏览量")
115+
@PostMapping("/posts/{id}/view")
116+
public ApiResponse<Boolean> increasePostViewCount(@PathVariable Long id) {
117+
siteContentService.increasePostViewCount(id);
118+
return ApiResponse.success(Boolean.TRUE);
119+
}
120+
107121
/**
108122
* 查询前台分类列表。
109123
*

yunyu-server/src/main/java/com/ideaflow/yunyu/module/site/service/SiteContentService.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.ideaflow.yunyu.module.site.service;
22

3+
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
34
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
45
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
56
import com.fasterxml.jackson.databind.JsonNode;
@@ -184,6 +185,29 @@ public SitePostDetailResponse getPostDetail(String slug) {
184185
return response;
185186
}
186187

188+
/**
189+
* 增加文章浏览量。
190+
* 作用:在前台文章详情页完成客户端加载后,按文章 id 执行一次原子自增,
191+
* 避免详情读取接口与浏览量统计逻辑耦合。
192+
*
193+
* @param id 文章 id
194+
*/
195+
public void increasePostViewCount(Long id) {
196+
if (id == null) {
197+
throw new BizException(ResultCode.BAD_REQUEST, "文章不存在");
198+
}
199+
200+
int affectedRows = postMapper.update(null, new LambdaUpdateWrapper<PostEntity>()
201+
.setSql("view_count = COALESCE(view_count, 0) + 1")
202+
.eq(PostEntity::getId, id)
203+
.eq(PostEntity::getStatus, "PUBLISHED")
204+
.eq(PostEntity::getDeleted, 0));
205+
206+
if (affectedRows <= 0) {
207+
throw new BizException(ResultCode.NOT_FOUND, "文章不存在");
208+
}
209+
}
210+
187211
/**
188212
* 查询前台分类列表。
189213
*

yunyu-web/app/assets/svg/csdn.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

yunyu-web/app/components/motion/YunyuParticleTree.vue

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
<script setup lang="ts">
22
import YunyuParticleTreeDark from '~/components/motion/YunyuParticleTreeDark.vue'
3-
import YunyuParticleTreeLight from '~/components/motion/YunyuParticleTreeLight.vue'
43
54
/**
65
* 云屿首页氛围组件包装层。
76
* 作用:根据当前明暗主题切换不同的首页装饰实现,
8-
* 让暗黑模式保留树影,明亮模式改为轻量云朵,并在切换主题时强制重新渲染。
7+
* 让暗黑模式保留树影,明亮模式不再额外渲染装饰层,并在切换主题时强制重新渲染。
98
*/
109
interface YunyuParticleTreeProps {
1110
treeAreaRatio?: number
@@ -15,6 +14,7 @@ interface YunyuParticleTreeProps {
1514
}
1615
1716
const colorMode = useColorMode()
17+
const renderVersion = ref(0)
1818
const props = withDefaults(defineProps<YunyuParticleTreeProps>(), {
1919
treeAreaRatio: 0.42,
2020
maxTreeWidth: 760,
@@ -24,12 +24,12 @@ const props = withDefaults(defineProps<YunyuParticleTreeProps>(), {
2424
2525
/**
2626
* 计算当前应该渲染的氛围组件。
27-
* 作用:将明亮模式和暗黑模式的视觉语言完全拆开,避免一个组件内兼顾两套样式
27+
* 作用:仅在暗黑模式下渲染树影装饰,明亮模式直接返回空,避免浅色云层在亮底上失去存在感
2828
*
2929
* @returns 当前主题对应的装饰组件
3030
*/
3131
const currentAtmosphereComponent = computed(() => {
32-
return colorMode.value === 'dark' ? YunyuParticleTreeDark : YunyuParticleTreeLight
32+
return colorMode.value === 'dark' ? YunyuParticleTreeDark : null
3333
})
3434
3535
/**
@@ -39,12 +39,25 @@ const currentAtmosphereComponent = computed(() => {
3939
* @returns 当前主题对应的组件 key
4040
*/
4141
const currentAtmosphereKey = computed(() => {
42-
return colorMode.value === 'dark' ? 'yunyu-particle-tree-dark' : 'yunyu-particle-tree-light'
42+
return `${colorMode.value === 'dark' ? 'yunyu-particle-tree-dark' : 'yunyu-particle-tree-empty'}-${renderVersion.value}`
4343
})
44+
45+
/**
46+
* 监听主题实际值与用户偏好变化。
47+
* 作用:在明亮、暗黑、系统三种模式切换时强制增加版本号,
48+
* 确保首页氛围组件一定会被重新挂载,而不是只复用旧实例。
49+
*/
50+
watch(
51+
() => [colorMode.value, colorMode.preference],
52+
() => {
53+
renderVersion.value += 1
54+
}
55+
)
4456
</script>
4557

4658
<template>
4759
<component
60+
v-if="currentAtmosphereComponent"
4861
:is="currentAtmosphereComponent"
4962
:key="currentAtmosphereKey"
5063
v-bind="props"

yunyu-web/app/components/motion/YunyuParticleTreeDark.vue

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ const TREE_DYNAMIC_PROFILE: TreeRenderProfile = {
111111
leafDepthAlpha: 0.06,
112112
leafSoftBaseAlpha: 0.024,
113113
leafSoftDepthAlpha: 0.036,
114-
particleAlphaScale: 0.42
114+
particleAlphaScale: 0.52
115115
}
116116
const TREE_STATIC_PROFILE: TreeRenderProfile = {
117117
branchGlowBaseAlpha: 0.004,
@@ -125,7 +125,7 @@ const TREE_STATIC_PROFILE: TreeRenderProfile = {
125125
leafDepthAlpha: 0.026,
126126
leafSoftBaseAlpha: 0.011,
127127
leafSoftDepthAlpha: 0.02,
128-
particleAlphaScale: 0.22
128+
particleAlphaScale: 0.42
129129
}
130130
131131
let animationFrameId: number | null = null
@@ -332,18 +332,29 @@ function createTreeScene(width: number, height: number, paddingLeft: number, pad
332332
0.02
333333
)
334334
335-
const particleCount = width < 520 ? 4 : 7
335+
const particleCount = width < 520 ? 5 : 8
336+
const leafAnchors = leaves.length > 0 ? leaves : [{
337+
x: areaLeft + areaWidth * 0.72,
338+
y: height * 0.34,
339+
radius: 1,
340+
revealAt: 0,
341+
driftPhase: 0,
342+
driftAmplitude: 0
343+
}]
336344
337345
for (let index = 0; index < particleCount; index += 1) {
346+
const anchorLeaf = leafAnchors[Math.floor(random() * leafAnchors.length)]
347+
const endBias = 0.58 + random() * 0.34
348+
338349
particles.push({
339-
x: areaLeft + areaWidth * (0.22 + random() * 0.62),
340-
y: height * (0.22 + random() * 0.42),
341-
radius: 0.45 + random() * 0.92,
342-
alpha: 0.06 + random() * 0.09,
343-
driftX: 1.4 + random() * 4.2,
344-
driftY: 1.1 + random() * 3.4,
350+
x: anchorLeaf.x + (random() - 0.22) * 18 + areaWidth * 0.08 * endBias,
351+
y: anchorLeaf.y + (random() - 0.5) * 16 - height * 0.015 * endBias,
352+
radius: 0.5 + random() * 0.96,
353+
alpha: 0.09 + random() * 0.14,
354+
driftX: 1 + random() * 3,
355+
driftY: 0.8 + random() * 2.2,
345356
phase: random() * Math.PI * 2,
346-
speed: 0.14 + random() * 0.22
357+
speed: 0.1 + random() * 0.16
347358
})
348359
}
349360
@@ -838,7 +849,7 @@ onBeforeUnmount(() => {
838849
--yy-tree-branch-glow-rgb: 68, 120, 172;
839850
--yy-tree-leaf-rgb: 85, 98, 114;
840851
--yy-tree-leaf-soft-rgb: 68, 81, 98;
841-
--yy-tree-dust-rgb: 114, 128, 144;
852+
--yy-tree-dust-rgb: 132, 145, 160;
842853
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.98) 0%, rgba(0, 0, 0, 0.95) 56%, rgba(0, 0, 0, 0.36) 72%, transparent 100%);
843854
}
844855
@@ -847,7 +858,7 @@ onBeforeUnmount(() => {
847858
--yy-tree-branch-glow-rgb: 72, 128, 182;
848859
--yy-tree-leaf-rgb: 93, 107, 123;
849860
--yy-tree-leaf-soft-rgb: 71, 86, 104;
850-
--yy-tree-dust-rgb: 118, 132, 148;
861+
--yy-tree-dust-rgb: 138, 152, 168;
851862
}
852863
853864
.yunyu-particle-tree__canvas {

yunyu-web/app/composables/useSiteContent.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ export function useSiteContent() {
6565
return await apiClient.request<SitePostDetail>(`/api/site/posts/${slug}`)
6666
}
6767

68+
/**
69+
* 上报文章浏览量。
70+
*
71+
* @param id 文章 id
72+
* @returns 是否上报成功
73+
*/
74+
async function increasePostViewCount(id: number) {
75+
return await apiClient.request<boolean>(`/api/site/posts/${id}/view`, {
76+
method: 'POST'
77+
})
78+
}
79+
6880
/**
6981
* 查询文章评论列表。
7082
*
@@ -164,6 +176,7 @@ export function useSiteContent() {
164176
getHome,
165177
listPosts,
166178
getPostDetail,
179+
increasePostViewCount,
167180
listPostComments,
168181
createPostComment,
169182
listCategories,

0 commit comments

Comments
 (0)