Skip to content

Commit 7210f30

Browse files
committed
Add CSCC-DB-Rucbase-2025 project
1 parent 624a708 commit 7210f30

10 files changed

Lines changed: 278 additions & 108 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
2+
version = 1
3+
name = "rushdb-lab.github.io"
4+
5+
[setup]
6+
script = ""
7+
8+
[[actions]]
9+
name = "Run"
10+
icon = "run"
11+
command = "npm run dev"

public/RMDB.webp

23.2 KB
Loading

public/Redbook.png

-129 KB
Binary file not shown.

src/components/astro/ProjectsSection.astro

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,44 @@ const { t } = getI18n(lang);
5050
</div>
5151
</div>
5252
</a>
53+
54+
<a
55+
href="https://github.com/RushDB-Lab/CSCC-DB-Rucbase-2025"
56+
class="card-link"
57+
target="_blank"
58+
rel="noopener noreferrer"
59+
>
60+
<div class="project-item featured-project">
61+
<div class="project-header">
62+
<div class="project-icon">
63+
<Image src="/RMDB.webp" alt="CSCC-DB-Rucbase-2025" loading="lazy" decoding="async" width={48} height={48} />
64+
</div>
65+
<div class="project-meta">
66+
<div class="project-title">{t('projects.rucbase2025.title')}</div>
67+
<div class="project-tags">
68+
<span class="project-tag">{t('projects.rucbase2025.tags.database')}</span>
69+
<span class="project-tag">RucBase</span>
70+
<span class="project-tag">TPC-C</span>
71+
<span class="project-tag">{t('projects.rucbase2025.tags.openSource')}</span>
72+
</div>
73+
</div>
74+
</div>
75+
<div class="project-desc">{t('projects.rucbase2025.desc')}</div>
76+
<div class="project-features">
77+
<div class="feature-item">
78+
<span class="feature-icon">🎓</span>
79+
{t('projects.rucbase2025.features.school')}
80+
</div>
81+
<div class="feature-item">
82+
<span class="feature-icon">🏷️</span>
83+
{t('projects.rucbase2025.features.team')}
84+
</div>
85+
<div class="feature-item">
86+
<span class="feature-icon">📈</span>
87+
{t('projects.rucbase2025.features.tpcc')}
88+
</div>
89+
</div>
90+
</div>
91+
</a>
5392
</div>
5493
</section>

src/components/blog/RelatedPosts.astro

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
import { getCollection, type CollectionEntry } from 'astro:content';
33
import { getI18n, type Lang } from '../../i18n';
44
5+
const relatedPostsCache = new Map<Lang, Promise<CollectionEntry<'blog'>[]>>();
6+
7+
function getPostsByLang(lang: Lang) {
8+
let cachedPosts = relatedPostsCache.get(lang);
9+
if (!cachedPosts) {
10+
cachedPosts = getCollection('blog', ({ id }) => id.startsWith(`${lang}/`));
11+
relatedPostsCache.set(lang, cachedPosts);
12+
}
13+
return cachedPosts;
14+
}
15+
516
interface Props {
617
currentPost: CollectionEntry<'blog'>;
718
lang: Lang;
@@ -12,9 +23,9 @@ const { currentPost, lang, maxPosts = 3 } = Astro.props;
1223
const { t } = getI18n(lang);
1324
1425
// Get all posts in the same language
15-
const allPosts = await getCollection('blog', ({ id }) => {
16-
return id.startsWith(`${lang}/`);
17-
});
26+
const allPosts = await getPostsByLang(lang);
27+
const currentPostTimestamp = currentPost.data.pubDate.getTime();
28+
const currentTags = new Set(currentPost.data.tags);
1829
1930
// Calculate relevance score for each post
2031
function getRelevanceScore(post: CollectionEntry<'blog'>): number {
@@ -28,7 +39,6 @@ function getRelevanceScore(post: CollectionEntry<'blog'>): number {
2839
}
2940
3041
// Matching tags: +1 point per match
31-
const currentTags = new Set(currentPost.data.tags);
3242
for (const tag of post.data.tags) {
3343
if (currentTags.has(tag)) {
3444
score += 1;
@@ -37,7 +47,7 @@ function getRelevanceScore(post: CollectionEntry<'blog'>): number {
3747
3848
// Recency bonus: more recent posts get slight preference
3949
const daysDiff = Math.abs(
40-
(post.data.pubDate.getTime() - currentPost.data.pubDate.getTime()) / (1000 * 60 * 60 * 24)
50+
(post.data.pubDate.getTime() - currentPostTimestamp) / (1000 * 60 * 60 * 24)
4151
);
4252
if (daysDiff < 30) score += 0.5;
4353
if (daysDiff < 7) score += 0.5;

src/components/blog/TableOfContents.astro

Lines changed: 92 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -29,85 +29,111 @@ const filteredHeadings = headings.filter(h => h.depth <= 3);
2929
</nav>
3030

3131
<script>
32-
function initTOC() {
33-
const tocLinks = document.querySelectorAll('.toc-link');
34-
const indicator = document.querySelector('.toc-indicator') as HTMLElement;
32+
(() => {
33+
let cleanupToc: (() => void) | undefined;
3534

36-
if (!tocLinks.length || !indicator) return;
35+
function initTOC() {
36+
if (cleanupToc) cleanupToc();
3737

38-
const headingElements = Array.from(tocLinks).map(link => {
39-
const slug = link.getAttribute('data-heading-slug');
40-
return document.getElementById(slug || '');
41-
}).filter(Boolean) as HTMLElement[];
38+
const tocRoot = document.querySelector('.toc');
39+
if (!tocRoot) return;
4240

43-
let activeLink: Element | null = null;
41+
const tocList = tocRoot.querySelector('.toc-list');
42+
const indicator = tocRoot.querySelector('.toc-indicator') as HTMLElement | null;
43+
const tocLinks = Array.from(tocRoot.querySelectorAll<HTMLAnchorElement>('.toc-link'));
4444

45-
function updateActiveHeading() {
46-
const scrollY = window.scrollY;
47-
const offset = 120;
45+
if (!tocList || !indicator || tocLinks.length === 0) return;
4846

49-
let currentHeading: HTMLElement | null = null;
47+
const linksBySlug = new Map<string, HTMLAnchorElement>();
48+
for (const link of tocLinks) {
49+
const slug = link.dataset.headingSlug;
50+
if (slug) linksBySlug.set(slug, link);
51+
}
5052

51-
for (const heading of headingElements) {
52-
if (heading.offsetTop - offset <= scrollY) {
53-
currentHeading = heading;
54-
}
53+
const headingElements = Array.from(linksBySlug.keys())
54+
.map((slug) => document.getElementById(slug))
55+
.filter(Boolean) as HTMLElement[];
56+
if (headingElements.length === 0) return;
57+
58+
let activeLink: HTMLAnchorElement | null = null;
59+
let scrollRaf: number | null = null;
60+
61+
function setActiveLink(nextLink: HTMLAnchorElement | null) {
62+
if (!nextLink || nextLink === activeLink) return;
63+
if (activeLink) activeLink.classList.remove('active');
64+
nextLink.classList.add('active');
65+
activeLink = nextLink;
66+
67+
const linkRect = nextLink.getBoundingClientRect();
68+
const listRect = tocList.getBoundingClientRect();
69+
const top = linkRect.top - listRect.top;
70+
indicator.style.transform = `translateY(${top}px)`;
71+
indicator.style.height = `${linkRect.height}px`;
72+
indicator.style.opacity = '1';
5573
}
5674

57-
if (currentHeading) {
58-
const slug = currentHeading.id;
59-
const newActiveLink = document.querySelector(`.toc-link[data-heading-slug="${slug}"]`);
60-
61-
if (newActiveLink && newActiveLink !== activeLink) {
62-
// Remove active class from previous
63-
if (activeLink) {
64-
activeLink.classList.remove('active');
65-
}
66-
67-
// Add active class to new
68-
newActiveLink.classList.add('active');
69-
activeLink = newActiveLink;
70-
71-
// Update indicator position
72-
const linkRect = newActiveLink.getBoundingClientRect();
73-
const tocRect = document.querySelector('.toc-list')?.getBoundingClientRect();
74-
75-
if (tocRect) {
76-
const top = linkRect.top - tocRect.top;
77-
indicator.style.transform = `translateY(${top}px)`;
78-
indicator.style.height = `${linkRect.height}px`;
79-
indicator.style.opacity = '1';
80-
}
75+
function updateActiveHeading() {
76+
const scrollY = window.scrollY;
77+
const offset = 120;
78+
let currentHeading: HTMLElement | null = null;
79+
80+
for (const heading of headingElements) {
81+
if (heading.offsetTop - offset <= scrollY) currentHeading = heading;
8182
}
83+
if (!currentHeading) return;
84+
85+
const nextLink = linksBySlug.get(currentHeading.id) || null;
86+
setActiveLink(nextLink);
8287
}
83-
}
8488

85-
// Smooth scroll on click
86-
tocLinks.forEach(link => {
87-
link.addEventListener('click', (e) => {
88-
e.preventDefault();
89-
const slug = link.getAttribute('data-heading-slug');
90-
const target = document.getElementById(slug || '');
91-
if (target) {
92-
const offset = 100;
93-
const targetPosition = target.offsetTop - offset;
94-
window.scrollTo({
95-
top: targetPosition,
96-
behavior: 'smooth'
97-
});
98-
}
99-
});
100-
});
89+
function onScroll() {
90+
if (scrollRaf !== null) return;
91+
scrollRaf = window.requestAnimationFrame(() => {
92+
updateActiveHeading();
93+
scrollRaf = null;
94+
});
95+
}
10196

102-
// Initial check and scroll listener
103-
updateActiveHeading();
104-
window.addEventListener('scroll', updateActiveHeading, { passive: true });
105-
}
97+
function onTocClick(event: Event) {
98+
const target = event.target as HTMLElement | null;
99+
const link = target?.closest<HTMLAnchorElement>('.toc-link');
100+
if (!link) return;
101+
event.preventDefault();
106102

107-
// Initialize on page load
108-
document.addEventListener('DOMContentLoaded', initTOC);
109-
// Re-initialize on Astro page transitions
110-
document.addEventListener('astro:page-load', initTOC);
103+
const slug = link.dataset.headingSlug;
104+
const heading = slug ? document.getElementById(slug) : null;
105+
if (!heading) return;
106+
107+
window.scrollTo({
108+
top: heading.offsetTop - 100,
109+
behavior: 'smooth',
110+
});
111+
}
112+
113+
tocRoot.addEventListener('click', onTocClick);
114+
window.addEventListener('scroll', onScroll, { passive: true });
115+
updateActiveHeading();
116+
117+
cleanupToc = () => {
118+
tocRoot.removeEventListener('click', onTocClick);
119+
window.removeEventListener('scroll', onScroll);
120+
if (scrollRaf !== null) {
121+
window.cancelAnimationFrame(scrollRaf);
122+
scrollRaf = null;
123+
}
124+
};
125+
}
126+
127+
if (document.readyState === 'loading') {
128+
document.addEventListener('DOMContentLoaded', initTOC, { once: true });
129+
} else {
130+
initTOC();
131+
}
132+
document.addEventListener('astro:page-load', initTOC);
133+
document.addEventListener('astro:before-swap', () => {
134+
if (cleanupToc) cleanupToc();
135+
});
136+
})();
111137
</script>
112138

113139
<style>

src/i18n/ui.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ export const ui = {
8585
analysis: '性能分析工具',
8686
},
8787
},
88+
rucbase2025: {
89+
title: 'CSCC-DB-Rucbase-2025',
90+
tags: {
91+
database: '数据库',
92+
openSource: '开源',
93+
},
94+
desc: '2024 全国大学生计算机系统能力大赛数据库赛道全国一等奖作品,基于 RucBase 的数据库系统实现与性能优化。',
95+
features: {
96+
school: '队伍学校:成都理工大学',
97+
team: '队伍名称:RushDB',
98+
tpcc: 'TPC-C 测试结果:32,820 txns/min',
99+
},
100+
},
88101
},
89102
members: {
90103
title: '团队成员',
@@ -260,6 +273,19 @@ export const ui = {
260273
analysis: 'Performance Analysis Tools',
261274
},
262275
},
276+
rucbase2025: {
277+
title: 'CSCC-DB-Rucbase-2025',
278+
tags: {
279+
database: 'Database',
280+
openSource: 'Open Source',
281+
},
282+
desc: 'National First Prize project in the 2024 National College Computer System Capability Competition (Database track), focused on implementation and performance optimization based on RucBase.',
283+
features: {
284+
school: 'School: Chengdu University of Technology',
285+
team: 'Team: RushDB',
286+
tpcc: 'TPC-C: 32,820 txns/min',
287+
},
288+
},
263289
},
264290
members: {
265291
title: 'Team Members',
@@ -435,6 +461,19 @@ export const ui = {
435461
analysis: 'パフォーマンス分析ツール',
436462
},
437463
},
464+
rucbase2025: {
465+
title: 'CSCC-DB-Rucbase-2025',
466+
tags: {
467+
database: 'データベース',
468+
openSource: 'オープンソース',
469+
},
470+
desc: '2024年全国大学生コンピュータシステム能力大会(データベース競技)全国一等賞の作品で、RucBase を基盤に実装と性能最適化を行いました。',
471+
features: {
472+
school: '所属大学:成都理工大学',
473+
team: 'チーム名:RushDB',
474+
tpcc: 'TPC-C 結果:32,820 txns/min',
475+
},
476+
},
438477
},
439478
members: {
440479
title: 'チームメンバー',

0 commit comments

Comments
 (0)