测试文档:博客初体验

本文最后更新于:1 个月前

真正的勇气,不是不感到害怕,而是即便害怕也选择前行。

测试文档

2024 年 10 月 3 日

这篇文章确实发挥了一点作用呵,有用没用的内容都能往这里搁。

看起来我的博客需要这么一个垃圾桶。

  • 这是最初的博客测试文章,用来简单测试撰写功能

  • 今天重定义这篇文章:(2023/10/01 早)
  • 这是一篇测试文档,旨在通过实践和优化,探索和熟悉 Typora 工具的各种使用技巧功能

  • 通过编写和修改这篇文档,并不断尝试工具中的不同功能和选项,我们可以更好地理解 Typora 的强大之处,并运用它提供的方式来提高写作效率和舒适度。

在 Typora 中创建流程图

使用 Mermaid 语言:Mermaid 是一种简单易懂的文本描述语言,可以用来绘制流程图、序列图、甘特图等。你可以在 Typora 中使用 Mermaid 语言来创建流程图。以下是一个简单的示例:

graph LR
    A[开始] --> B[中间步骤]
    B --> C[结束]

子图(Subgraphs):使用子图可以将图表分组,并在子图中创建更详细的流程或子流程。例如:

graph LR

subgraph GroupA
A1 --> A2
A1 --> A3
end

subgraph GroupB
B1 --> B2
B1 --> B3
end

A2 --> B2
A3 --> B3

样式调整:Mermaid 允许你调整图表的样式,包括节点颜色、箭头样式、背景颜色等等。通过在代码中添加样式设置,可以使你的图表更具视觉吸引力。例如:

graph LR

style A fill:#88CCEE,stroke:#FFFFFF,stroke-width:2px
style B fill:#EE8866,stroke:#FFFFFF,stroke-width:2px
style C fill:#FFDD44,stroke:#FFFFFF,stroke-width:2px

A --> B
B --> C
C --> A

时序图(Sequence Diagram):除了流程图,Mermaid 还支持绘制时序图。时序图是用于展示对象之间消息传递顺序的图表。以下是一个简单的时序图示例:

sequenceDiagram
  A->>B: 请求数据
  B->>C: 获取数据
  C->>B: 返回数据
  B->>A: 显示数据

引用

这是一个简单的引用

当我们在博文中,引用了其他站点内的文章,可以在此说明

当我们在博文中引用其他站点内的文章时,需要按照学术规范进行引用并提供正确的引用格式。引用是一种重要的学术行为,可以展示对先前研究和观点的尊重,并且为读者提供参考来源以深入了解相关主题。在引用他人文章时,应遵循以下几点原则进行扩写和优化:

  1. 明确标注引用内容:在引用时,必须明确标示出被引用文章的作者、标题、出处、日期等信息。这样可以确保引用内容的准确性,并避免对原作者的不当侵犯。
  2. 使用适当的引用格式:根据引用风格指南(如 APA、MLA 等),将引用内容格式化为正确的引用格式。引用格式通常包括作者姓名、文章标题、出版日期、出版机构、链接等细节。
  3. 添加适当的引用标记:在引文中添加适当的引用标记,例如引号或斜体字,以区分引用内容和自己的原创内容。
  4. 提供引用页脚或参考文献:在博文的末尾或适当位置,列出引用的文献的详细信息。这样读者就可以根据提供的信息找到原文进行进一步阅读。

Carbon 效果测试

  • 这个效果看起来好奇怪(2023/11/13 早)
1
2
3
4
5
6
7
8
9
// 捕获 id = "leftZhankai"
Element leftZhankai = doc.getElementById("leftZhankai");
Elements heads = leftZhankai.select(".sons .cont div:nth-of-type(2)");
for (Element head : heads) {
Elements title = head.select(">p:nth-of-type(2)");
// Elements author = head.select("p:nth-of-type(2)");
// System.out.println(title.text() + " " + author.text());
System.out.println("hhh" + title.text());
}

写给未来的一封信

2024 年 5 月 1 日

时光邮局 - 给未来的自己写一封信 (hi2future.com)

你好!这封信是我特意写给你的。

当你看到这封来信,说明它已经历经九个月的岁月年华,最终交付到了你的手中。

二零一四年秋,我第一次离家来到三官庙小学上学,那一年咱俩都是十二岁,上六年级。尽管六(1)班跟六(二)班的教室仅有一墙之隔,

你有过别人给你写过信的经历吗,应该是没有的,当然我也没有过。在这个物欲横流的社会中,人与人之间的交流愈发方便却略显麻木。

最近还在为某些即将到来却又不得不面对的琐事感到焦虑吗?印象中咱俩就这个话题也面对面聊过很多次了。二零二三年三月十七号晚,咱俩第一次在餐厅里坐着吃饭聊天。这一聊就是三个多小时,从六点半唠到了晚上快十点。我近段时间开始喜欢上了阅读和写作,只是想在这尘世之间留下我生活过的证据,或者仅仅是为了在闲暇时刻打发时间。偶尔我也会打打游戏或者刷刷短视频,在简单快乐的低趣味消磨时光。但在更多的时间里,我会选择在歌单里找一首钟爱的音乐,在富有节奏的律动中开始写作。此刻可以说我的耳边是嘈杂的或者吵闹的,但我的内心平静如水。

大家都希望过能够长久地跟自己信任的人呆在一起,期盼着他们会在自己最需要陪伴的时候,呆在我们身边。这种微不足道的陪伴,有时却往往是我们能够坚持继续生活下去的动力。他们可以是自己的亲人,也可以是亲密无间的同学或朋友。指尖触摸着热乎的手机屏幕,发送出一条条热情的消息,却不会让冰冷的现实变得更加温热。

话到嘴边却难出口,情到深处是孤独。

还有三个多月就要毕业了,我们的大学生涯也在逐渐步入尾声。今天是三月一号,这个时间节点确实没有代表着特别的意义。不过我特意查去了下日历,知道今天是二月初二。在这个普通的周六晚间,其实早在五一长假期间,我就已经基本完成了这封信的写作,但我却选择把它在六月一号这天寄出。为什么呢?也许是无心之举,想让时间冲刷淡忘这片记忆,让它在我无感知的情况下寄出可能是最好的。当然也有可能仅仅为了凑个“九”而已,谁又会知道呢。

不过这些都已经不重要了。

祝你生活愉快!

公众号

上古神话 - 饕餮

饕餮,是中国古代神话传说中的一种凶恶贪食的野兽,也是四大凶兽之一。其外貌特征十分独特且引人注目。

图片

饕餮的形状如羊身人面,但其目在腋下,这样的设计使得它的外貌更加狰狞和神秘。它的牙齿像虎牙一样尖锐,爪子也像虎爪一样锋利。饕餮的嘴巴略弯曲内勾,仿佛随时准备吞噬一切。

图片

图片

图片

饕餮的身躯庞大,据说有数十丈长,而它的头颅居然占了整个身躯的一半。同时,饕餮发出的声音如婴儿般,这种声音与其凶猛的外貌形成了鲜明的对比,使得它更加神秘和不可预测。

图片

图片

在性格上,饕餮的贪食程度令人震惊。它具有巨大的食欲,能够吞噬任何东西,包括人类、动物和物体。饕餮无止境地追求食物,从而引发了破坏和混乱。尽管饕餮带有恶意的形象,但它也被视为一种对抗邪恶的力量。

图片

图片

饕餮的外貌十分醒目,它的头部硕大,占据了整个身体的大部分比例。眼睛炯炯有神,仿佛能洞察一切,但眼中却透露着凶恶与贪婪。嘴巴张开时,尖锐的牙齿仿佛能够咬碎一切阻碍它满足食欲的障碍。

图片

图片

图片

饕餮不仅外形凶猛,其性格也极为贪婪。它对食物的欲望几乎达到了疯狂的程度,无论是人类、动物还是其他生物,只要被饕餮发现,都会成为它的猎物。

图片

总的来说,饕餮是中国古代神话中的一个重要角色,其独特的外貌和性格使其成为贪欲和暴力的象征。饕餮的形象和象征意义在古代文化中扮演了重要的角色,警示人们远离贪欲和暴力的诱惑。

上古神话 - 梼杌

梼杌(TaoWu):在《神异经·西荒经》的记载中,梼杌是西方荒原上的一头神秘而凶恶的异兽。

图片

图片

图片

它的体型硕大无朋,远超过普通的老虎,仿佛一座移动的小山。梼杌的毛发长而浓密,如同黑色的绸缎,闪烁着幽暗的光泽。

图片

图片

这些毛发不仅为它提供了保护,还让它在夜晚的荒原中显得更为神秘莫测。其身躯健壮,肌肉虬结,充满了无尽的力量

图片

图片

图片

它的面部呈现出一种近似人类的特征,但眼神却冷酷无情,透露出凶恶和残忍。梼杌的脚爪如同虎爪一般锐利,能够轻易撕裂猎物。

图片

图片

它的獠牙长而尖锐,闪烁着寒光,是其在战斗中的致命武器。最引人注目的是它那异常长的尾巴,不仅用于保持平衡,还能够在战斗中发挥巨大的作用,如同一条巨大的鞭子,抽打之间能够造成毁灭性的伤害。

图片

图片

梼杌在荒原上肆意行凶作乱,它的出现总是伴随着血与火的灾难。它残忍无情,以猎杀其他生灵为乐,无论是凶猛的野兽还是弱小的生物,都无法逃脱它的魔爪。

图片

图片

图片

它的存在,让人们感到无尽的恐惧和绝望。在荒原上,梼杌的传说被广为流传,成为了人们心中无法磨灭的阴影。图片

图片

梼杌不仅是一头凶恶的异兽,更是凶恶的象征

上古神话 - 穷奇

穷奇,这只《山海经》中记载的古老怪兽,它的形象深深烙印在每一个听闻其传说的人心中。其外形犹如一只体型巨大的老虎,但更为引人注目的是,它的背部生长着一对宽阔的翅膀。

图片

图片

这对翅膀强健有力,羽毛呈现出深邃的黑色,闪烁着金属般的光泽,使得穷奇能够在广阔的天空中自由翱翔,成为真正的空中霸主

图片

图片

图片

穷奇的形象在各种传说中存在着细微的差异,但无一不强调其凶猛和邪恶的特性。它的眼睛深邃而冷酷,仿佛能洞察人心最深处的黑暗。

图片

图片

獠牙尖锐而锋利,闪烁着寒光,仿佛随时准备撕咬猎物。它的爪子犹如钢铁般坚硬,能够轻易撕裂任何阻碍

图片

图片

穷奇不仅外形恐怖,更有着独特的性格。它只听取恶人的甜言蜜语,对于好话则充耳不闻。在穷奇的眼中,善良和正义都是无用的,只有邪恶和贪婪才是它追求的真理。

图片

图片

穷奇的行动毫无规律可循,它时而隐匿在深山老林之中,时而出现在繁华的市井之间。每当它出现时,都会带来一片恐慌和血腥。它的翅膀遮天蔽日,獠牙和利爪闪烁着寒光,令所有生物都感到恐惧和不安。

图片

图片

图片

穷奇成为了恶行的代表,它的存在让人们感到无尽的恐惧和绝望。它的传说被广为流传,成为了人们心中无法磨灭的阴影。在穷奇面前,所有的善良和正义都显得如此脆弱和无力,只有无尽的恐惧和绝望才是人们最真实的感受。

图片

图片

上古神话 - 蛊雕

蛊雕,一只神秘莫测的异兽,其外形融合了雕的优雅与未知的恐怖。

图片

它的身形如同展翅高飞的雕,但头部却生长着一对异常尖锐且弯曲的双角,这对角闪烁着幽暗的光芒,仿佛能够刺破一切阻碍,赋予蛊雕一种与生俱来的威严与力量。

图片

图片

蛊雕的叫声独特而诡异,宛如婴儿的啼哭,初听之下纯真无邪,但仔细品味,却能感受到其中蕴含的深深恶意与无尽贪婪。每当这诡异的啼哭声响起,周围的生物都会不由自主地感到一股寒意,仿佛死神的阴影正在悄然逼近

图片

图片

蛊雕的栖息地是深邃而广袤的雷泽,那里的水域为它提供了丰富的食物来源。然而,蛊雕并不满足于在水域中称王称霸,它时常展翅高飞,飞越云层,来到古时的黎云荒原狩猎人类。

图片

图片

但每过十年,它就会从沉睡中苏醒,开始一场疯狂的狩猎。当它醒来之际,荒原上的人类将会面临一场灾难,无数生命将在它的利爪和獠牙下消逝。这种恐怖的习性让蛊雕成为了人类心中的噩梦,每当它苏醒的年份临近,都会引发一片恐慌和不安

图片

更为奇特的是,蛊雕有一个与众不同的习性。它常年处于沉睡之中,仿佛在与世隔绝的梦境中度过漫长的岁月。

图片

白日射金阙,虎豹九关开

在茂密的丛林深处,一只凶猛残暴的老虎巨兽静静潜伏。

图片

图片

它的身躯犹如一座移动的山峦,肌肉线条在月光下显得异常坚硬而有力,仿佛是由最坚硬的钢铁锻造而成。

图片

图片

那金黄色的皮毛,在夜色中闪烁着幽暗的光芒,仿佛每一根毛发都蕴含着无尽的野性和力量。

图片

图片

上古神话 - 凤凰

凤凰,这传说中的神鸟,以其优雅的身姿和绚丽的羽毛,在人们心中留下了深刻的印记。

图片

它似乎从古老的画卷中跃然而出,身披五彩斑斓的羽毛,每一片都散发着淡淡的光华,宛如天边初升的朝霞,又似深海中闪烁的珍珠。

图片

图片

凤凰的头部高昂,一双晶莹的眼眸仿佛能洞察世间万物,深邃而神秘。它的喙如弯月,闪烁着金属般的光泽,透出无尽的威严与力量。颈部细长而优雅,轻轻摆动间,似乎在诉说着千年的故事

图片

图片

它的身体矫健而有力,每一次振翅,都仿佛能掀起一阵风暴。翅膀上的羽毛层层叠叠,宛如一幅精致的画卷,色彩绚烂而又不失和谐。在阳光的照耀下,这些羽毛闪烁着耀眼的光芒,仿佛能照亮整个世界。

图片

图片

图片

凤凰不仅仅是一种神鸟,更是一种精神的象征。它代表着吉祥、和谐与美好,是人们心中永远的向往和追求。

图片

每当人们看到凤凰的身影,都会感到一种莫名的激动和敬仰,仿佛被它的美丽和力量所震撼。

图片

如神似魔,麒麟现世

麒麟,作为中国古代神话传说中的瑞兽,其形象独特且富有深厚的文化内涵。麒麟是以鹿类的特征为主,同时融合了龙、牛、马的成分而形成的一种神秘生物。

图片

图片

从外部形状上看,麒麟集多种动物特征于一身。它的头部类似于龙头,但更显得温和而庄重;身躯则如鹿身,矫健而优雅;眼睛炯炯有神,如同狮眼般锐利;全身覆盖着龙鳞,闪烁着金属般的光泽;腿部则有着马蹄的矫健和力量;尾部则如同牛尾,长而有力,挥动间仿佛能扫尽一切邪恶。

图片

图片

麒麟在中国古代文化中有着极高的地位。它被视为吉祥瑞兽,代表着吉祥、和平、繁荣和美好。据古籍记载,只有当世上有着大善之人的存在时,麒麟才会出现。

图片

图片

因此,麒麟的出现往往预示着天下太平、五谷丰登。同时,麒麟也被赋予了很高的德行,它不践踏任何生物,以露水和天上掉下来的食物为生,性情温和而善良。

图片

图片

总的来说,麒麟作为中国古代神话传说中的瑞兽,其形象独特、文化内涵丰富。它代表着吉祥、和平、繁荣和美好,是中国传统文化中不可或缺的一部分。图片

图片

深居俯夹城

1、深居俯夹城,春去夏犹清

图片

图片

2、晴日暖风生麦气,绿阴幽草胜花时

图片

图片

3、连雨不知春去,一晴方觉夏深

图片

图片

4、小荷才露尖尖角,早有蜻蜓立上头

图片

图片

5、首夏犹清和,芳草亦未歇

图片

图片

雾柳暗时云度月

1、柳外轻雷池上雨,雨声滴碎荷声

图片

图片

2、人皆苦炎热,我爱夏日长

图片

图片

3、云尽月如练,水凉风似秋

图片

图片

4、小扇引微凉,悠悠夏日长

图片

图片

5、雾柳暗时云度月,露荷翻处水流萤

图片

图片

日出江花红胜火

1、日出江花红胜火,春来江水绿如蓝

图片

图片

2、太清上初日,春水送孤舟

图片

图片

3、一轮顷刻上天衢,逐退群星与残月

图片

图片

4、日照香炉生紫烟,遥看瀑布挂前川

图片

图片

5、人言落日是天涯,望极天涯不见家

图片

图片

废稿

2024 年 6 月 10 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
最近几周正逢期末结课,好些代课老师们都布置了期末作业,个个都跟催命佛一般叫唤着要验收大家的作业成果。

好巧不巧,上节课因为老师事先没有讲清楚作业的具体要求,导致我们小组课堂展示的PPT演示被评为不合格,演讲的同学还没讲完就被代课老师撵了下来。

因为有我们组的前车之鉴,后来的小组都针对性地做足了准备,课堂展示都很顺利,获得的分数也都普遍不低。

那我们组可算倒大霉了。这代课老师规定每个小组只能有一次展示机会,私下里再听听上届学长学姐对他的评价,搞不好这老家伙想让我们小组每个人直接挂科。

我当然是没有这个顾虑的,既然是你事先沟通有问题让我们吃了哑巴亏,现在连争取第二次机会都不允许,只能由这老家伙说了算了罢。

不过就因为这点小作业挂人,我是不会相信的。毕竟大学课程一个代课老师再怎么不负责任,课堂容错率也不应该这么低的。

我就是这么想的,近段时间估计很难再抽出时间来好好休息了,更别提跟那家伙斗智斗勇了。

他要怎么办是他自己的事,我实在没那闲工夫去操心,只要做了自己该做的仁至义尽就行了。

我朋友却不这么认为,他说今天回去以后,私下里咱还是要多花点时间把这个作业好好完善下,再找那老师说说好话看能不能过。

该放低姿态就放低姿态嘛,对吧。下周去了把咱完善的作品拿上去演示演示,看他的意思是到底行不行,好话多说两句让人家看到咱们的态度。

他说的不无道理,但是以及已经有人这么做了,结果很显然是没有成功,那老师不吃这一套的。

那我认为我们就没必要再花时间搞这么个演示了,等候他下一步的指示就行。至于稍微花时间完善下这个作业,倒也不是不可以,兜个底总没坏处,做好以后扔给那家伙看看就行了。

结果我朋友非跟我抠字眼,他说事情是不能这么办的,你得放低姿态人家说什么就是什么,咱还是得好好听人家的对吧。

我说对对,我当然明白你是什么意思,作业完善以后给他看看不就行了,不过仔细想想其实并没什么必要吧,再找他只会是自取其辱。

那怎么能行,你得让人家看到你的态度吧。就说老师是我们小组之前的态度不够好,这个作业完成的确实也不太用心,没有达到您的要求。之前确实是我们小组的问题,我们好好反省过已经认识到自己的错误了,现在又重新做了这么一份报告,您能不能抽时间看看这份作业怎么样。

就这么一段话,他噼里啪啦给我反复念叨了三五遍,那诚恳低迷的态度,让我感觉我好像真的做了什么很对不起别人的事情。

我当然明白他的意思,却总有一种说不出来的难受。

最后我只好依了他,答应会照他说的做,下周来上课的时候,上去好好给那代课老师赔礼道歉去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 精神寄托

> 2024年6月10日

后来才明白:

精神寄托可以是音乐,可以是书籍,可以是工作,可以是山川湖海。

唯独不可以是人。

音乐,是我疗伤的良药;

书籍,成为我智慧的源泉;

工作,是我实现价值的舞台;

而大自然,是我心灵的归宿。

友链

2024 年 7 月 16 日

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
items:
- {
title: 'Memory的个人博客',
intro: '心若有所向往, 何惧道阻且长',
link: 'https://deng-2022.gitee.io/blog/',
avatar: '/img/memory.jpg'
}
- {
title: '下次一定',
intro: '免费观看电影和剧集',
link: 'https://www.iiice.cn/#/',
avatar: '/img/icon/xcyd.png'
}
- {
title: '商汤秒画',
intro: 'AI 帮你作画',
link: 'https://miaohua.sensetime.com/?ref=87dhw.cn',
avatar: '/img/icon/stmh.png'
}
- {
title: "Squoosh",
intro: "免费好用的图片压缩网站",
link: "https://squoosh.app/",
avatar: "/img/avatar.png"
}
- {
title: "87导航网",
intro: "免费好用的图片压缩网站",
link: "https://87dhw.cn/",
avatar: "/img/avatar.png"
}
- {
title: "极简壁纸",
intro: "免费好用的图片压缩网站",
link: "https://bz.zzzmh.cn/index",
avatar: "/img/avatar.png"
}
- {
title: "时光邮局",
intro: "免费好用的图片压缩网站",
link: "https://www.hi2future.com/",
avatar: "/img/avatar.png"
}
- {
title: "电影推荐",
intro: "免费好用的图片压缩网站",
link: "https://www.mvcat.com/",
avatar: "/img/avatar.png"
}
- {
title: "彼岸图网",
intro: "免费好用的图片压缩网站",
link: "https://pic.netbian.com/",
avatar: "/img/avatar.png"
}
- {
title: "技术摘抄",
intro: "丰富的UI组件",
link: "https://learn.lianglianglee.com/",
avatar: "/img/avatar.png"
}
- {
title: "小林coding",
intro: "丰富的UI组件",
link: "https://www.xiaolincoding.com/",
avatar: "/img/avatar.png"
}
- {
title: "超级简历",
intro: "丰富的UI组件",
link: "https://www.wondercv.com/cvs",
avatar: "/img/avatar.png"
}
- {
title: "Ant Design",
intro: "丰富的UI组件",
link: "https://ant-design.antgroup.com/components/overview-cn",
avatar: "/img/avatar.png"
}
- {
title: "Ant Design Pro",
intro: "丰富的高级菜单",
link: " https://pro.ant.design/zh-CN/docs/overview/",
avatar: "/img/avatar.png"
}
- {
title: "Element",
intro: "丰富的UI组件",
link: "https://element.eleme.cn/#/zh-CN/component/installation",
avatar: "/img/avatar.png"
}
- {
title: "Mybatis-Plus",
intro: "Mybatis-Plus官方文档",
link: "https://baomidou.com/",
avatar: "/img/avatar.png"
}
- {
title: "MVN Respository",
intro: "提供丰富的插件和依赖",
link: "https://mvnrepository.com/",
avatar: "/img/avatar.png"
}
- {
title: "Spring",
intro: "Spring官网",
link: "https://spring.io/projects/spring-boot/",
avatar: "/img/avatar.png"
}
- {
title: "Redis",
intro: "Redis官网",
link: "https://redis.io/docs/getting-started/",
avatar: "/img/avatar.png"
}
- {
title: "Yarn",
intro: "Yarn中文文档",
link: " https://yarn.bootcss.com/",
avatar: "/img/avatar.png"
}
- {
title: "Vant",
intro: "轻量、可定制的移动端Vue组件库",
link: "https://vant-contrib.gitee.io/vant/#/zh-CN/",
avatar: "/img/avatar.png"
}
- {
title: "Vue Router",
intro: "Vue.js 的官方路由",
link: "https://router.vuejs.org/zh/introduction.html",
avatar: "/img/avatar.png"
}
- {
title: "Knife4j",
intro: "一键生成开发文档",
link: "https://doc.xiaominfo.com/docs/quick-start",
avatar: "/img/avatar.png"
}
- {
title: "Easy Excel",
intro: "Excel处理工具",
link: "https://easyexcel.opensource.alibaba.com/",
avatar: "/img/avatar.png"
}

吐槽

2024 年 8 月 13 日

昨天下午心情差了极点,咆哮着写下这段文字:

1
这什么情况,为什么我等了这么长时间,吐槽了这么久,甚至都没有给我机会,在这里根本就听不进去,根本学不进去,我真的是受够了。没完了吗,为什么今天了还没有结束这个破事,时间拖得越长对我就越不利,我实在无法想象我的秋招学习环境竟然会是现在这个样子。这怎么学,没法学对吧,这怎么复习,根本就没有心情复习,今天都快过去了,为什么还是没有结果,难道连发声的机会都不给吗?这太蛮横无礼了,我真的不知道从早到晚我呆在这里的意义究竟是什么,如果有立刻离开这里的机会,我真的会毫不犹豫,可为什么连发声的机会都没有。妈的,我的学习环境太差劲了,我根本就适应不了,不能总是这样叫天天不应叫地地不灵的,这简直就是精神折磨。我能怎么办,我只能在这里无能狂怒,在这里写字叫嚣,那家伙就是不给我任何机会和时间,我仅存的一点耐心都快被消磨殆尽了。我的心情简直差到了极点,为什么最后一段学习生涯会是现在这个样子。如果早就听说是这样的结果,我或许会在暑期期间就找另一个合适的实习岗位,而不是在这里混吃等死。一点都学不进去,一点学习效率都没有妈的,每天浑浑噩噩连觉都睡不好,这算什么几把实训基地。

投递

2024 年 8 月 20 日

每次投递简历,总要一遍遍复制粘贴我的项目和实习经历介绍,真是太麻烦了,我直接粘贴在这里:

1
山西中维信息工程股份有限公司 - Java 后端开发 研发一部 2024.03 - 2024.07
1
2
参与并主导了基于 Spring Boot + uniapp 的企业级电子名片管理系统的开发,负责数据校验核对、接口开发以及系统
性能优化等工作,为企业用户提供了高效便捷的电子名片管理平台。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
工作内容:
负责电子名片项目的数据校验核对和接口开发,通过引入索引、缓存机制及 SQL 优化等手段优化名片管理系统的查
询接口,显著提升了查询性能并减少响应时间。
参与学习并应用企业版后端项目模板,开发代码自动生成工具,能够根据数据库表结构自动生成 CRUD 接口代码。
针对名片列表加载缓慢的问题,提出采用分页加载与懒加载技术相结合的策略,通过代码重构和使用 WebSocket 进
行实时数据推送,成功将名片列表加载时间缩短了30%,有效降低了服务器压力并提升了用户体验。
为解决首次访问系统的用户主页加载过慢的问题,使用 Spring Scheduler 定时任务来实现缓存预热,并通过分布式
锁保证多机部署时定时任务不会重复执行。
遵循部门代码提交与审核规范,积极参与前后端对接协调工作,确保接口定义的一致性和数据交互的顺畅,深度参与
项目全周期。

业务理解:
使用 Swagger 并结合 @Api、@ApiOperation 等注解,实现了接口信息的自动化生成与深度文档化,显著提升了
API 文档的可读性和开发团队的工作效率。
引入Docker容器化技术,简化了项目部署流程,实现了从开发到生产环境的一键部署,大大提高了项目上线效率。
熟练运用 Git 进行版本管理,在禅道积极提交并跟踪 Bug,配合团队内部开发人员协同解决并验收,完成自测整改
计划。
1
MemoryAPI 忆汇廊 - 开发者文档:http://deng-2022.atomgit.net/memory-api-doc/
1
2
3
4
5
6
7
8
9
10
11
12
13
项目介绍
这个项目是一个基于 Spring Cloud + React 的全栈微服务架构接口开放平台,该平台提供丰富的 API 接口服务,并配备高效的接口管理与调试工具,方便开发者快速集成和使用,提升开发效率。

主要工作
定制 Spring Boot 初始化模板,包括全局请求响应拦截、全局异常处理、Swagger + Knife4j 接口文档生成、跨域支持及多环境配置等核心功能。
自主设计获取随机壁纸、随机生成诗词等多样化接口服务,涵盖多种应用场景和需求,并提供有详尽的接口文档方便开发者快速集成。
使用 Spring Cloud Gateway 实现微服务网关,统一流量入口,实现聚合文档、路由转发、统一认证授权和请求限
制,确保接口稳定性。
自主实现 API 签名认证,为每个注册用户分配唯一密钥对(accessKey 和 secretKey)。用户请求时须附带密钥,服务端通过 SHA256 算法生成签名并验证,确保请求来源合法且数据完整。
利用 Maven 聚合工程和自动化构建功能,根据业务逻辑和职责将原有单体应用拆分为多个独立的模块,实现微服务架构转型。
使用 Dubbo RPC 框架抽取和优化公共服务,避免引入复杂的业务逻辑以确保网关职责单一性,实现了网关模块的轻量化和服务间解耦。
应用 Spring Boot 的自动配置机制,自主设计客户端 SDK 实现高效的接口调用,大幅简化平台接入流程和开发效率。

1
MemorySearch 忆搜阁 - 开发者文档:http://deng-2022.atomgit.net/memory-search-doc/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
项目介绍
这是一个基于 Spring Boot + Elastic Stack 技术栈 + Vue.js 的聚合搜索中台。它不仅是一个强大的搜索引擎,更是
一个内容丰富的社区平台,能够提供一站式的搜索、管理和互动体验,满足各种用户需求。

主要工作
通过 Spring AOP 切面编程和自定义注解抽象公共服务,对方法执行前后进行统一处理,实现权限校验和全局请求拦
截,并进一步简化日志管理。
采用 JWT 作为 Session 的替代方案,结合 Spring Boot 框架深度集成和 JWT 的携带式令牌机制,进一步简化了用户
身份认证流程。
配置定时任务,使用 Hutool 和 gson 获取外源热榜博文 id,并使用 jsoup 实时抓取文章数据,利用 CSS Selector
解析 HTML 并入库。
深入学习 Elastic Stack 技术,掌握 Elasticsearch 的倒排索引和分词机制, 能够使用查询 DSL 语言编写复杂的搜索条
件,成功规划并实施关键词语高亮、搜索词条建议以及热门话题分析等特性。
通过适配器模式封装数据源接口,结合门面模式实现搜索结果聚合,利用注册器模式动态切换聚合搜索服务,显著简化
前后端开发和调用过程。
自主搭建 Logstash 数据同步管道并掌握其相关配置和优化,实现定时同步 MySQL 的变更数据记录到 ES,具备丰富
的数据同步实战经验。
使用 Jsoup 库从外部网站抓取大量诗词数据,并使用 CompletableFuture 异步编程大幅减少数据库批量插入的等待时
间,性能提升近70%。

回忆录

2024 年 1 月 6 日

这些记忆真的很零散,每件小事都可以聊很多的。这里快速罗列一下“我的回忆清单”:

童年

学校

西彻

来胡家小卖部,摔炮,擦炮,啥都有卖

理发,我妈就是天生的理发师,一把好手

过年放炮、听戏,大学校的戏台子,贴对联,一家人大扫除,里里外外都是过年新气象,拿炭火垒小火堆,吃瓜子水果看春晚,除夕晚上打王者,好热闹!

八架山和睦村,学前班的生活,仅仅生活了一年,却感觉很漫长,经历的事情也很多:放电影,开学第一天,小偷

呐,八架山转回来之后还得适应,邓家洼小学是这辈子唯一的净土,太多欢乐的事情了

邓家洼小学,学校集体活动:院子里背书、除草、拉木炭柴火,搞防震演练,站队,王老师讲话

王老师很有意思,自己家不动手,让我们打扫,哈哈哈

很喜欢冬日的清晨,天短夜长

过六一,编排节目,姥姥门前唱大戏,很快乐,老师们都很用心,三关庙表演,完了还在饭店里吃了顿好的。教室旁边的小屋里有个电视,我俩跳得好,当排头。

最喜欢在教室里打卡、玩飞行棋了,对每个老师都有深刻印象,王老师,毛毛,邓老师,张金烂,辛艳梅,李平安

平时基本没有考试,有时候老师们组织期中考试,数学很差劲,期末考试就去

看云识天气,星期五的天气永远是最好的,外学校的同学们也都回来了,下课就能直接跑回去玩电脑了

周末总是很欢乐的,在家里玩电脑,出去跳皮筋,打卡,拉几个好朋友在村里面闲逛

说到周末,就不得不提一嘴假期了,可以玩的有很多,放假啦!当然还有假期作业啊,“二十篇日记”

侠盗猎车手-罪恶都市,最喜欢玩的游戏,植物大战僵尸,大鱼吃小鱼,忍者神龟。红警,我的世界,轴心与同盟,使命召唤

小时候没啥游戏,电脑自带的植物大战僵尸,每周都盼着哥哥回来下载新的游戏:流星蝴蝶剑、孤胆枪手、侠盗飞车

爱看的电视节目也很多,金鹰卡通,少儿频道,中央十台的法律讲堂、一线、有几部剧很深刻,过年,香包什么死在我怀里

喜欢玩小昆虫,蚂蚁,蜘蛛,荧光小屁虫赛跑,抓蜗牛

给王老师擦洗摩托、换窗户贴纸、抽水

早上上课去的早,王老师没起床,站在门外等他倒尿开门。经常看见建宏跟死人一样早读。

喜欢折纸

教材,英才教程,确实一部好教材,就是光研究里面的生字读写了。还有俩本学校发的教材,王老师卖给我们。新华字典。

人生中第一次体测,很不适应,没把我们跑死,下去测身高体重,作仰卧起坐

学校以前还有个水房来着,那边以前还是个教师,龙龙他妈在里边烧火做饭,后来关掉了。旁边是个办公室,老师换了好几个了,六一那会儿是真的快乐,那家伙在里面换衣服脱得精光。

唱歌,新来的老师,亲爱的战士你不要想家,不要想妈妈~

西彻,秘密基地,打草打一天

那会矿上要求规程,爸爸就搬个小凳子小方桌在西彻路灯下写,他说凉快

说到路灯,还记得刚修路灯那会儿,全村到处都是工人,上下学路上都能在路边捡几个螺丝钉和漂亮的小零件

磁铁,U 型磁铁和条形磁铁,当然磁铁都是偷来的,碰碰撞撞打断好几个,我还记得用飞行器换回来两个条形磁铁的壮举

偷东西,那家伙喜欢偷东西,随手拿橡皮被张老师逮住,拿了我两把尺子,一块橡皮

我家院子,那几棵树,厕所还换了两次位置,院里砌了墙,最后也没有安上大门,哈哈

后院里种庄稼,韭菜,西红柿,葱啥都有,桃树、杏树

当然收庄稼也很有趣,黄豆都是要借台机子放在西彻,一晚上开工,稻穗是要摊开来在路上或者西彻,拿摩托碾过来碾过去

门外的油柏马路,那条狗子踩下的脚印,那段时间都不能走大路的

提到狗子,又得聊聊龙龙家的那条虎子了,母狗,一胎能生十来二十个,一个都活不下来,很有意思

最喜欢下雨打雷天了,夏天的记忆最深刻了,太阳大的吓人,热死了,下雨又很凉爽,当然还有冰雹

冬天也很有趣,我是一只来自北方的狼,那片瓷砖也有点来头,上面做过很多游戏

拍视频,兄弟俩拍了很多视频。

在村里瞎逛:村头的小学,村里最下边沟里

夏天在外面开灶,洗衣服,污水就顺着那道沟渠流出大门,流到街上了,晚上摆个桌子就在院子里吃饭了,蚊子多

爱好很多,折纸,橡皮泥,飞行棋,用自己手画的地图玩,打卡,放大镜烧蚂蚁,当然还有玩泥巴,抓小蜜蜂

秋天有啥好玩的呢?打枣子

喜欢拍视频,拿个诺基亚就能拍,说到手机,我家也换过很多部手机了,每部都能讲讲,那几部破手机可是能玩 MC 的啊

闯过很多祸,很顽皮呵,上屋顶,纵火,还老喜欢拉裤子

受过很多伤,拿刀子划拉自己,坐摩托不专心,脚后跟塞后轮里被搅烂

我家开小卖铺啦,有机会就偷偷吃点小零食,每周还得去进货,爸妈不在家就去奶奶家

会做饭也是种本事,有了不去奶奶家的接口

奶奶家很温馨啊,印象中在那里过夜很多次,吃奶奶给做的饭,好几次起寺院吃汤水都在这里,那会人还很多,晚上还踩过稻穗。总奶奶家秘密很多,电视节目没啥意思,一百多个台,一般不能看,爷爷喜欢看戏,打仗,我那时候不喜欢

很晕车,但每次出门都让坐车

你们为什么属羊呢?我奶奶在我小时候捏了一只羊,从此我就属羊了,你们为什么属羊呢?

最害怕的东西,毛擦擦,夏夜里很凉快,毛擦擦也多

大哥十二岁生日那几天,玩的很开心

邓家洼最后的记忆就是王家坡开学前一晚,在后院烧小火堆

走出邓家洼后的记忆就没那么新鲜了,不过还是有些事情值得回忆,镇上饭馆吃饭,坐三轮回家

这是我今天早上的清单,后面会逐步补充,随着时间的推移,那些深埋于内心深处的记忆都能被挖掘出来

2024 年 1 月 29 日

拉裤子里,印象中上厕所不知道往厕所跑,经常拉裤子里

2024 年 3 月 9 日

对于这个话题,要讲起来是很费力的。我从小在这里长大,从幼儿园到小学到初中再到高中,这里承载了我太多太多的回忆。那过去的二十年岁月中经历过的人和事,是绝不可能用寥寥数语就能言说的。

现在,虽然我对儿时的邓家洼村仅存的概念日渐模糊,但往日的回忆却也慢慢浮现于心头,且愈发清晰和强烈了。随着年岁的增长,我愈发怀念小时候的欢快时光,更迫切地想把这些故事写下来,那是属于我的童年的故事。那么,从什么时候开始,我对童年的记忆变得模糊了呢?显而易见,在搬离村里定居到成家庄镇旁的大肚皮小区之后,几乎就在那一瞬间,我的人生仿佛被劈裂为两个阶段。原来那个在村里天真烂漫、无忧无虑戏水玩泥巴的我突然逃掉了。那个呆在村子里仿佛永远也长不大的我,逃到了一个永远也不会被找到的地方,无影无踪了。

在那个平凡的星期六的下午,我们哥俩跟往常一样,换好鞋子背上书包,关了灯锁了门,从家里出发走过大街皮,坐上我妈的小摩托去到镇上坐成班车,去到燎原大厦前坐上 1 路公交直达四中坡底。我们住在村里,要去县城上高中很麻烦,每次假期结束返校都是走这样的流程,跟过去一年多里的岁月一样;我们也才在读高二,学习上还没有抓的那么紧,在外读书一周最多两周就能回来一次,跟过去五年多里的岁月一样;今天下午去了教室得赶紧补补作业,坚持一周把这该死的学业磨完就能回家了。等晚上天快黑了回到黑乎乎的院子里,还没开门就又能闻到从厨房里飘来的妈妈做饭的清香了。在吃饱喝足后换洗衣服,听着洗衣机“轰隆隆”的运作声,就又能上炕钻进被窝里热乎乎地打游戏了,跟过去十几年里的岁月是一样的

二零一九年十一月五号下午,我去柳林县城里读高二,那年我十七岁。

十七岁那年,我的童年结束了。

也许有用

2024 年 9 月 23 日

WebSocket 实操

理解 WebSocket 的工作流程对于搞清楚代码中的逻辑是非常关键的。WebSocket 是一种全双工通信协议,允许客户端和服务器之间在单个 TCP 连接上进行双向数据传输。它的一个显著特点是,连接一旦建立,双方可以随时互相发送数据,无需每次发送数据都重新建立连接。

在你的代码中,WebSocket 相关的工作流程是通过继承 WebSocketListener 并实现其中的几个回调方法来完成的。我们逐个解释这些方法,帮助你理清 WebSocket 的工作流程。

1. WebSocket 工作流程概览

  1. 连接建立:客户端发起 WebSocket 连接,服务器同意连接并确认。此时,双方可以开始发送数据。
  2. 消息发送/接收:客户端和服务器在连接建立后可以互相发送消息。
  3. 连接关闭:客户端或服务器可以随时主动关闭连接,或由于错误等原因导致连接断开。

在你的代码里,几个关键的回调函数实现了这些步骤:

2. onOpen - 连接建立后

1
2
3
4
5
6
@Override
public void onOpen(WebSocket webSocket, Response response) {
System.out.print("大模型:");
MyThread myThread = new MyThread(webSocket);
myThread.start();
}
  • 触发时机:当 WebSocket 连接成功建立时,服务器确认了连接,客户端和服务器可以开始发送/接收数据。
  • 作用onOpen 方法被调用后,程序开始通过 webSocket 向服务器发送请求。
    • 这里创建了一个新的线程 MyThread 来执行发送请求的任务。这个线程持有当前的 WebSocket 连接,随后会发送请求数据给服务器。
    • 通过这个设计,你可以在连接打开后马上发送请求,而不需要等待额外的用户操作。

3. onMessage - 收到服务器消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
public void onMessage(WebSocket webSocket, String text) {
// 解析返回的数据
JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
if (myJsonParse.header.code != 0) {
System.out.println("发生错误,错误码为:" + myJsonParse.header.code);
webSocket.close(1000, "");
}
List<Text> textList = myJsonParse.payload.choices.text;
for (Text temp : textList) {
System.out.print(temp.content); // 输出大模型的回答
totalAnswer += temp.content; // 汇总大模型的回答
}
if (myJsonParse.header.status == 2) { // 状态码为2时,表示结束
System.out.println();
System.out.println("*************************************************************************************");
if (canAddHistory()) { // 如果能加入历史记录
RoleContent roleContent = new RoleContent();
roleContent.setRole("assistant");
roleContent.setContent(totalAnswer);
historyList.add(roleContent); // 保存大模型的回复
} else {
historyList.remove(0); // 移除最早的对话
RoleContent roleContent = new RoleContent();
roleContent.setRole("assistant");
roleContent.setContent(totalAnswer);
historyList.add(roleContent);
}
wsCloseFlag = true; // 关闭 WebSocket
totalFlag = true; // 准备下一次输入
}
}
  • 触发时机:当服务器向客户端发送消息时,onMessage 回调被触发。
  • 作用
    1. 解析服务器的返回:服务器返回的消息通过 WebSocket 传递给客户端,消息以字符串 text 的形式返回。代码中使用 Gson 库将 text 解析为 JsonParse 对象,这是服务器的响应内容。

    2. 处理消息内容:提取出服务器返回的文本内容(textList),并输出到控制台。大模型的回复会被保存到 totalAnswer 中,逐步构建一个完整的回答。

    3. 结束对话:当 status == 2 时,表示服务器已经返回了完整的结果,此时程序可以关闭连接。

    4. 历史记录的处理:程序检查是否可以加入新的对话历史(通过 canAddHistory()),如果历史对话超过设定的长度限制,就会移除最早的几条对话。然后保存最新的大模型回答。

4. onFailure - 连接失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
try {
if (null != response) {
int code = response.code();
System.out.println("onFailure code:" + code);
System.out.println("onFailure body:" + response.body().string());
if (101 != code) {
System.out.println("connection failed");
System.exit(0); // 终止程序
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
  • 触发时机:当连接由于某种原因失败(例如网络问题、服务器故障)时,onFailure 被调用。
  • 作用
    • 处理连接失败的场景,比如服务器没有正确响应或者发生异常错误。
    • response.code() 返回了 HTTP 状态码,101 是正常的 WebSocket 握手响应。如果状态码不是 101,则表示连接出现了问题,并打印相关错误信息。
    • 出现失败时,程序会调用 System.exit(0) 终止运行。

5. onClose - 连接关闭(未直接实现)

虽然代码中没有实现 onClose,但 WebSocket 提供了 onClose 方法来处理连接关闭的逻辑。这个方法通常在连接被客户端或服务器主动关闭时触发,或者在连接空闲超时后自动触发。

例如:

1
2
3
4
@Override
public void onClose(WebSocket webSocket, int code, String reason) {
System.out.println("Connection closed. Code: " + code + ", Reason: " + reason);
}

6. MyThread - 发送消息线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyThread extends Thread {
private WebSocket webSocket;

public MyThread(WebSocket webSocket) {
this.webSocket = webSocket;
}

public void run() {
try {
JSONObject requestJson = new JSONObject();
// 构建请求参数,包含header、parameter和payload
// 将历史记录和新问题加入到消息体中
webSocket.send(requestJson.toString());
// 等待服务端返回完毕后关闭
while (true) {
Thread.sleep(200); // 等待接收消息
if (wsCloseFlag) {
break;
}
}
webSocket.close(1000, ""); // 关闭 WebSocket
} catch (Exception e) {
e.printStackTrace();
}
}
}
  • 作用
    • MyThread 是一个辅助类,用于发送数据。它将用户的输入问题和对话历史封装成 JSON 格式,发送到服务器。
    • 发送完请求后,线程会进入一个循环,不断检查 wsCloseFlag 是否为 true,表示服务器已经完成返回并关闭连接。

7. WebSocket 的工作流程总结

  1. 客户端请求连接main() 方法中,客户端发起 WebSocket 连接。

  2. 服务器确认连接onOpen 方法被触发,连接成功后,MyThread 开始向服务器发送请求。

  3. 服务器返回消息:服务器处理完请求后,onMessage 方法会被触发,客户端接收到服务器的回答并解析、展示给用户。

  4. 处理完成并关闭连接:当服务器返回结果时,程序会检查是否可以关闭连接,并在必要时通过 webSocket.close() 主动关闭连接。

  5. 异常处理:如果连接失败或出现错误,onFailure 会处理相关逻辑。

通过这几个核心回调方法,程序能够实现稳定、完整的 WebSocket 通信流程。


是的,你理解得完全正确!

这段代码实现了一个客户端,它通过 WebSocket 向服务端发送请求,并处理服务端的响应。

具体流程是:

  1. 客户端发起请求
    main() 方法中,客户端通过 OkHttpClient 建立与服务端的 WebSocket 连接。在连接成功后(onOpen 被调用),客户端通过 MyThread 向服务端发送数据,通常包含用户问题及对话历史。

  2. 服务端处理请求并返回响应
    服务器收到客户端的请求后,会进行处理,生成回应内容(如大模型的回答),然后通过 WebSocket 通道发送回客户端。

  3. 客户端处理服务端响应
    服务端发回消息后,客户端的 onMessage 方法会被触发,接收到服务端的回应。客户端解析服务端返回的消息(如大模型的文本回答),输出给用户,并存储到对话历史中。

  4. 处理完毕后,客户端关闭连接
    当服务端表示对话结束时,客户端根据状态(如 status == 2),关闭 WebSocket 连接。

你可以这样理解:

  • 客户端负责:发起请求、发送数据、处理服务端响应、关闭连接。
  • 服务端负责:处理客户端请求、生成响应并通过 WebSocket 返回给客户端。

这是一种典型的客户端-服务器模式,WebSocket 用于在客户端和服务器之间保持一个长连接,使得双方可以高效地双向传输数据。


要用 WebSocket 实现一个简单的聊天室功能,核心思路是通过 WebSocket 连接来实现多个客户端之间的实时消息传递。WebSocket 提供了客户端和服务器之间的双向通信,非常适合实现实时聊天场景。

实现步骤概述

1. 服务端实现

  • 服务端需要能够管理多个客户端的 WebSocket 连接,并处理消息广播。
  • 服务端接收到一个客户端的消息后,将该消息发送给所有已连接的其他客户端(包括自己)。

2. 客户端实现

  • 客户端通过 WebSocket 连接到服务端,发送消息给服务端。
  • 当客户端收到服务端转发的消息时,显示在自己的聊天界面中。

具体实现

我们将分别实现服务端和客户端。


1. 服务端实现(Java WebSocket Server)

使用 javax.websocketSpring Boot 实现一个简单的 WebSocket 服务器。这里以 javax.websocket 实现为例。

服务端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/chat")
public class ChatServer {
private static Set<Session> clients = new CopyOnWriteArraySet<>();

@OnOpen
public void onOpen(Session session) throws IOException {
// 有客户端连接,加入连接集合
clients.add(session);
System.out.println("New client connected: " + session.getId());
}

@OnMessage
public void onMessage(String message, Session session) throws IOException {
// 收到客户端消息并广播给所有客户端
System.out.println("Received message from " + session.getId() + ": " + message);
broadcastMessage(message, session);
}

@OnClose
public void onClose(Session session) {
// 客户端断开连接,移除连接
clients.remove(session);
System.out.println("Client disconnected: " + session.getId());
}

@OnError
public void onError(Session session, Throwable throwable) {
// 处理错误
System.out.println("Error from client: " + session.getId());
throwable.printStackTrace();
}

private void broadcastMessage(String message, Session senderSession) throws IOException {
// 遍历所有连接的客户端,发送消息
for (Session client : clients) {
if (client.isOpen()) {
client.getBasicRemote().sendText("Client " + senderSession.getId() + ": " + message);
}
}
}
}

服务端说明:

  • **@ServerEndpoint("/chat")**:表示 WebSocket 的访问路径是 /chat,所有连接到这个路径的客户端都会在同一个“聊天室”中。
  • **onOpen()**:当客户端连接时,会触发该方法,服务端将新连接的 Session 保存到 clients 集合中。
  • **onMessage()**:当服务端接收到某个客户端发送的消息时,触发该方法,服务端会调用 broadcastMessage() 将消息广播给所有客户端。
  • **onClose()**:当客户端断开连接时,触发该方法,移除客户端的 Session
  • **broadcastMessage()**:遍历所有已连接的客户端,将消息发送给它们。

启动服务:可以使用 TomcatJetty 等容器来部署这个服务端代码,或者用 Spring Boot 嵌入式服务器。


2. 客户端实现(Java 客户端)

客户端使用 OkHttpjavax.websocket 来建立 WebSocket 连接并发送消息。

客户端代码:

使用 OkHttp 实现简单的 WebSocket 客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import okhttp3.*;

import java.util.Scanner;

public class ChatClient extends WebSocketListener {
private static final String SERVER_URL = "ws://localhost:8080/chat";

public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(SERVER_URL).build();
WebSocket webSocket = client.newWebSocket(request, new ChatClient());

Scanner scanner = new Scanner(System.in);
System.out.println("Enter your message: ");

while (true) {
String message = scanner.nextLine();
if (message.equalsIgnoreCase("exit")) {
webSocket.close(1000, "Exiting");
break;
}
webSocket.send(message);
}
}

@Override
public void onOpen(WebSocket webSocket, Response response) {
System.out.println("Connected to chat server.");
}

@Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("Message received: " + text);
}

@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
System.out.println("Closing: " + reason);
webSocket.close(1000, null);
}

@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
t.printStackTrace();
}
}

客户端说明:

  • OkHttp 的 WebSocket 实现:这里使用 OkHttp 的 WebSocket 实现来创建客户端并与服务端进行通信。
  • 发送消息:用户可以在控制台输入消息,消息会通过 WebSocket 发送到服务端。
  • 接收消息:收到的消息会显示在控制台,onMessage() 回调会被触发。
  • 关闭连接:当用户输入 exit 时,WebSocket 连接会被关闭。

3. 客户端 HTML(使用浏览器进行聊天)

如果你想在网页上实现聊天,可以用纯 HTML 和 JavaScript 来连接 WebSocket 服务器。

HTML 和 JavaScript 客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Chat</title>
</head>
<body>
<h1>Chat Room</h1>
<div id="chat">
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
</div>

<script>
const socket = new WebSocket('ws://localhost:8080/chat');

socket.onopen = function() {
console.log('Connected to server');
};

socket.onmessage = function(event) {
const messagesDiv = document.getElementById('messages');
const message = document.createElement('p');
message.textContent = event.data;
messagesDiv.appendChild(message);
};

socket.onclose = function(event) {
console.log('Disconnected from server');
};

socket.onerror = function(error) {
console.log('Error: ' + error.message);
};

function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value;
socket.send(message);
input.value = '';
}
</script>
</body>
</html>

HTML 客户端说明:

  • 通过 JavaScriptWebSocket API 建立连接并与服务端通信。
  • 当接收到消息时,将消息显示在网页上。
  • 用户可以输入消息并发送到服务端。

4. 总结

  • 服务端负责管理客户端的连接,接收并广播消息。
  • 客户端负责与服务端通信,发送和接收聊天消息。
  • 可以通过桌面客户端(如 Java 编写的客户端)或网页客户端(HTML + JavaScript)参与聊天。

通过这种方式,你就可以实现一个简单的聊天室应用,所有客户端都可以通过 WebSocket 实现实时的双向消息通信。


完全正确!你总结得非常到位。通过 WebSocket 实现的聊天室本质上就是这样的流程:

  1. 客户端连接到服务端:每个客户端都通过 WebSocket 连接到服务器。

  2. 客户端发送消息:当客户端用户输入消息并发送时,这条消息通过 WebSocket 发送到服务端。

  3. 服务端接收并广播消息:服务端接收到消息后,会将这条消息广播给所有连接的客户端(包括发送消息的客户端),这样所有人都能实时看到。

  4. 每个客户端展示收到的消息:每个客户端收到服务端转发的消息后,显示在用户的聊天界面中。

这就是一个简单的聊天室的基本原理,其中 WebSocket 的优势就在于它能够保持长连接,允许服务端随时将消息推送给所有客户端,实现了实时的双向通信。

扩展想法

  • 你可以加入用户系统,让每个用户有自己的用户名,消息显示时可以显示发送者的名字。
  • 可以为消息加上时间戳,显示消息发送的具体时间。
  • 加入房间功能,允许用户加入不同的聊天室房间,让不同的用户群在不同的房间内聊天。

小结:

通过这个示例,你已经掌握了 WebSocket 实现实时通信的基础。使用 WebSocket 不仅可以做聊天室,还可以用于各种实时应用,比如在线游戏、股票市场数据推送、协同编辑工具等。


这是一个很好的问题!Tomcat WebSocket 实现Spring WebSocket 实现确实有所不同,虽然它们都依赖于 Java WebSocket API 进行基础通信,但两者的差异体现在功能、简便性以及对高级功能的支持上。

接下来,我将详细解释两者的区别,包括它们的架构、开发体验、功能扩展、适用场景等。


1. Tomcat WebSocket 实现

1.1 基础实现

Tomcat 提供了对 Java 原生 WebSocket API (javax.websocket) 的支持。通过 Tomcat,你可以直接使用这些原生 API 来实现 WebSocket 功能。

1.2 代码实现

典型的基于 Tomcat 的 WebSocket 实现,通常使用 javax.websocket 注解和接口来定义 WebSocket 服务端点。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/chat")
public class TomcatChatEndpoint {

@OnOpen
public void onOpen(Session session) {
System.out.println("New connection: " + session.getId());
}

@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("Received message: " + message);
session.getBasicRemote().sendText("Echo: " + message);
}

@OnClose
public void onClose(Session session) {
System.out.println("Connection closed: " + session.getId());
}

@OnError
public void onError(Session session, Throwable throwable) {
System.err.println("Error on connection: " + session.getId());
throwable.printStackTrace();
}
}

1.3 优点

  • 轻量级:Tomcat 的 WebSocket 实现是非常轻量的,它依赖 Java EE 标准的 javax.websocket API,直接和 Tomcat 容器集成。没有额外的框架负担,适合那些不需要复杂功能的小型项目。
  • 直接控制:你可以完全控制底层 WebSocket 连接,原生 API 提供了所有核心操作的细粒度控制。

1.4 缺点

  • 扩展功能不足:Tomcat 原生的 WebSocket 支持是基础的,缺乏高级功能。比如,它没有内置的消息转发机制、没有复杂的消息处理器(如 STOMP 等协议),以及对复杂架构(如集群支持、异步任务队列等)的支持也很有限。
  • 不太适合大规模应用:管理多个连接、群组消息处理和消息广播等功能需要开发者自行实现,开发复杂 WebSocket 应用可能会非常繁琐。

1.5 适用场景

  • 小型应用:如果你只是需要实现简单的实时通信功能,并且不依赖复杂的 WebSocket 功能(如协议扩展或消息处理器),Tomcat 的原生 WebSocket 实现非常适合。
  • 细粒度控制:对于那些希望有更多底层控制的开发者,比如直接管理连接状态、实现自定义的消息处理逻辑,Tomcat 的实现提供了非常灵活的接口。

2. Spring WebSocket 实现

2.1 基于 Spring 框架的高级实现

Spring WebSocket 是 Spring 框架基于 WebSocket 协议的一个扩展实现。它不仅封装了 Java 的原生 WebSocket API,还提供了一系列高级功能和集成支持,例如:

  • 消息处理器:支持通过控制器方式(类似于 Spring MVC)处理 WebSocket 消息。
  • 消息协议扩展:支持 STOMP 协议,可以处理订阅、消息广播、点对点消息等复杂场景。
  • 消息转换:内置 JSON/XML 消息的转换和处理功能,结合 Spring MVC 常用的 @MessageMapping 注解,可以轻松处理不同格式的消息。

2.2 代码实现

Spring WebSocket 的实现非常类似于 Spring MVC,开发体验更加简化。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

@MessageMapping("/chat")
@SendTo("/topic/messages")
public String processMessage(String message) {
System.out.println("Received message: " + message);
return "Broadcast: " + message;
}
}

2.3 主要功能

  • 支持 STOMP 协议:STOMP 是基于文本的消息协议,类似于 HTTP 的请求响应模型,但为 WebSocket 通信优化了消息发送、订阅、点对点等功能。

    • 通过 STOMP,客户端可以订阅主题,服务端可以向这些主题广播消息,或者发送点对点消息。
  • 消息处理:Spring WebSocket 使用 @MessageMapping 注解来映射 WebSocket 消息,类似于 Spring MVC 中的 @RequestMapping

  • 消息广播:通过 @SendTo 注解,你可以将处理后的消息广播给订阅了某个主题的客户端。

  • 集成 Spring Security:Spring WebSocket 支持与 Spring Security 集成,便于对 WebSocket 通信进行认证和授权。

2.4 优点

  • 开发体验良好:如果你熟悉 Spring MVC,Spring WebSocket 的开发方式非常类似,使用 @MessageMapping 等注解,可以极大简化开发。
  • 功能丰富:Spring WebSocket 不仅支持 WebSocket 协议,还支持 STOMP 协议扩展,实现了消息路由、广播、点对点通信等功能,适合复杂的实时通信场景。
  • 集成性强:可以与整个 Spring 生态系统(Spring Boot、Spring Security、Spring Data 等)无缝集成,便于开发复杂的大规模应用。
  • 异步处理:Spring WebSocket 提供异步消息处理的支持,能够更好地处理高并发下的消息接收与发送。

2.5 缺点

  • 相对复杂:Spring WebSocket 比原生的 Tomcat WebSocket 更加复杂,适合更高级的开发需求。如果仅是简单的 WebSocket 通信场景,Spring 的实现可能显得“过重”。
  • 依赖较多:Spring WebSocket 的功能丰富,但需要依赖 Spring 框架,这使得开发时需要引入更多的依赖库,启动时也会加载更多 Spring 相关的模块。

2.6 适用场景

  • 复杂应用场景:如果你的应用需要支持消息广播、点对点通信、订阅模型、认证和授权等复杂场景,Spring WebSocket 是一个非常好的选择。
  • 与 Spring 生态集成:如果你的项目已经基于 Spring 或 Spring Boot 构建,Spring WebSocket 能够无缝集成到你的项目中,极大提升开发效率和可维护性。

3. 总结与对比

特性 Tomcat WebSocket 实现 Spring WebSocket 实现
底层 API 基于 Java EE javax.websocket 原生 API 封装了 javax.websocket,并提供高级功能
消息协议支持 仅支持基础的 WebSocket 协议 支持 STOMP 等高级消息协议
开发复杂度 较低,直接使用原生 API 较高,但类似于 Spring MVC,适合复杂场景
消息广播与点对点通信 需要自己实现 内置 STOMP 支持,可以轻松实现广播和点对点通信
异步与并发支持 手动处理 内置异步和高并发处理机制
集成性 仅依赖于 Java EE 和 Tomcat 深度集成 Spring 生态系统,支持 Spring Boot、Spring Security
适用场景 适合轻量级、简单的 WebSocket 通信 适合大规模、复杂的实时通信应用场景
消息处理方式 基于 @OnMessageSession 手动处理 使用 @MessageMapping@SendTo 等注解简化处理

总结:

  1. Tomcat WebSocket 实现

    • 适合简单的实时通信场景,比如轻量的 WebSocket 聊天应用。
    • 需要开发者手动处理客户端连接、消息广播、点对点通信等功能。
    • 如果你希望有更多底层控制,比如手动管理连接、消息的收发,它更灵活。
  2. Spring WebSocket 实现

    • 适合需要高级功能的场景,比如支持订阅/广播模型、身份认证、集成 Spring 生态系统的大型应用。
    • 如果你熟悉 Spring MVC,Spring WebSocket 的开发体验更接近于 MVC,开发**

复杂 WebSocket 应用更加便捷**。

  • 支持消息协议扩展(如 STOMP),能够轻松实现订阅模型、广播和点对点消息等功能。

选择哪种方式取决于你的项目复杂度、对实时通信功能的需求以及你对 Spring 框架的熟悉程度。如果你的项目已经使用了 Spring 框架,那么选择 Spring WebSocket 会带来更多的便利和功能扩展。


好的,下面我会分别使用 Tomcat WebSocket 实现Spring WebSocket 实现 来编写一个完整的 WebSocket 服务端。两者的实现会从导入依赖、编写配置类、处理 WebSocket 消息、到运行应用的步骤进行详细讲解。


1. Tomcat WebSocket 实现

1.1 导入依赖

Tomcat WebSocket 实现依赖于 javax.websocket API,Tomcat 容器本身已经内置支持,不需要额外导入依赖。

但是,如果你是使用 Maven 来管理依赖,可以手动引入 WebSocket API 的依赖:

1
2
3
4
5
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>

1.2 WebSocket 配置类

基于 Tomcat 的原生 WebSocket API,不需要额外的配置类,只需在 WebSocket 端点类中通过注解配置路径即可。

1.3 WebSocket 端点实现

创建 WebSocket 端点类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/chat")
public class TomcatWebSocketServer {
private static Set<Session> clients = new CopyOnWriteArraySet<>(); // 存储所有连接的客户端

@OnOpen
public void onOpen(Session session) {
clients.add(session); // 新的客户端连接加入
System.out.println("New connection: " + session.getId());
}

@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("Received message: " + message);
// 广播消息到所有客户端
for (Session client : clients) {
if (client.isOpen()) {
client.getBasicRemote().sendText("Echo from server: " + message);
}
}
}

@OnClose
public void onClose(Session session) {
clients.remove(session); // 移除断开连接的客户端
System.out.println("Connection closed: " + session.getId());
}

@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("Error in session: " + session.getId());
throwable.printStackTrace();
}
}

说明:

  • @ServerEndpoint("/chat"):定义了 WebSocket 的端点 URL 为 /chat
  • 使用了 WebSocket 的生命周期回调函数:
    • @OnOpen:当客户端连接时触发。
    • @OnMessage:当收到客户端消息时触发,并将消息广播给所有已连接的客户端。
    • @OnClose:当连接关闭时触发。
    • @OnError:当发生错误时触发。

1.4 部署到 Tomcat

将该 WebSocket 服务部署到 Tomcat 服务器,具体步骤如下:

  1. 创建 web.xml 文件
    如果你使用标准的 Servlet 配置,确保在 WEB-INF/web.xml 中配置:

    1
    2
    3
    <web-app>
    <display-name>WebSocket Tomcat Example</display-name>
    </web-app>
  2. 打包项目
    使用 Maven 或其他工具将项目打包成 .war 文件。

  3. 部署到 Tomcat
    .war 文件放入 Tomcat 的 webapps 文件夹中,启动 Tomcat。

  4. 测试
    使用 WebSocket 客户端(如浏览器的开发者工具或专用的 WebSocket 测试工具)连接到 ws://localhost:8080/<your-app>/chat,发送消息并观察消息的广播效果。


2. Spring WebSocket 实现

2.1 导入依赖

使用 Spring WebSocket 的实现需要添加 Spring 的相关依赖。如果你使用 Maven,可以使用以下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
<!-- Spring Boot Starter WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<!-- 如果使用 Spring Boot 需要以下依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 如果使用 Spring WebSocket + STOMP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-messaging</artifactId>
</dependency>
</dependencies>

2.2 WebSocket 配置类

在 Spring 中,你需要创建一个配置类来启用 WebSocket 支持,并定义消息路由。

WebSocket 配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic"); // 订阅前缀
config.setApplicationDestinationPrefixes("/app"); // 客户端发送消息时前缀
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat-websocket") // WebSocket 端点 URL
.setAllowedOrigins("*") // 允许的跨域请求
.withSockJS(); // 支持 SockJS 回退机制
}
}

说明:

  • @EnableWebSocketMessageBroker:启用 WebSocket 消息代理支持。
  • enableSimpleBroker("/topic"):启用简单消息代理,使用 /topic 前缀处理广播消息。
  • setApplicationDestinationPrefixes("/app"):定义应用的消息前缀,客户端发送到服务器的消息需要加上 /app 前缀。
  • addEndpoint("/chat-websocket"):定义 WebSocket 的连接端点为 /chat-websocket,并支持 SockJS。

2.3 消息处理控制器

通过 Spring,WebSocket 消息的处理可以像处理 HTTP 请求一样,通过控制器来进行处理。

消息控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

@MessageMapping("/sendMessage")
@SendTo("/topic/messages")
public String processMessage(String message) {
System.out.println("Received message: " + message);
return "Broadcast message: " + message;
}
}

说明:

  • @MessageMapping("/sendMessage"):定义处理来自 /app/sendMessage 的 WebSocket 消息。
  • @SendTo("/topic/messages"):将处理后的消息广播给订阅了 /topic/messages 的客户端。

2.4 Spring Boot 主程序

如果你使用 Spring Boot,可以创建一个简单的主程序来启动应用。

1
2
3
4
5
6
7
8
9
10
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringWebSocketApplication {

public static void main(String[] args) {
SpringApplication.run(SpringWebSocketApplication.class, args);
}
}

2.5 前端代码示例

在前端,我们可以通过 WebSocket 或 SockJS 进行连接。示例使用 SockJS 和 Stomp.js 进行通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Spring WebSocket Chat</title>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.0/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
</head>
<body>
<h1>WebSocket Chat</h1>
<input type="text" id="message" placeholder="Enter your message" />
<button onclick="sendMessage()">Send</button>
<ul id="messages"></ul>

<script>
var stompClient = null;

function connect() {
var socket = new SockJS('/chat-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function (message) {
showMessage(JSON.parse(message.body));
});
});
}

function sendMessage() {
var message = document.getElementById('message').value;
stompClient.send("/app/sendMessage", {}, JSON.stringify(message));
}

function showMessage(message) {
var messages = document.getElementById('messages');
var li = document.createElement('li');
li.appendChild(document.createTextNode(message));
messages.appendChild(li);


}

window.onload = connect;
</script>
</body>
</html>

2.6 运行并测试

  • 启动 Spring Boot 项目。
  • 打开浏览器访问页面(如 http://localhost:8080),发送消息时,消息会广播给所有客户端。

总结:

  1. Tomcat WebSocket:使用 Java 原生 WebSocket API,适合轻量级、灵活的 WebSocket 应用。
  2. Spring WebSocket:集成了 Spring 的特性,支持 STOMP 消息协议,适合复杂的 WebSocket 应用,如群聊、点对点消息等。

这两个方式各有优缺点,适合不同类型的应用场景。

## 二面

你好!我是邓纬,我预约了周四下午14点的面试,视频会议链接目前还没有发送到我的邮箱,昨天和今天分别致电HR也没有回应,能帮我看一下吗。

如果因为系统繁忙导致视频链接没有发送至我的邮箱,烦请发送我新的视频会议面试链接;

如果是因为我这边操作有误导致错过了这次面试邀约,还请重新给我发送新的面试邀约。

如有疑问,请给我打电话:18535854762,这场面试对我很重要,谢谢了!

为什么这个人事()的电话打不通,加微信好友也没有任何回应,我只是想争取自己二面的机会。。可二面都通知我了为什么没有发视频会议链接。。

二面邀请:

img

我的操作是没有问题的,可会议链接到现在也没有发送:
img

编程之旅

2024 年 10 月 27 日

image-20241027170249099

投递

2024 年 11 月 1 日

image-20241101092930939

image-20241101092926461

image-20241104095921630


测试文档:博客初体验
https://test.atomgit.net/blog/2023/03/15/测试文档:博客初体验/
作者
Memory
发布于
2023年3月15日
更新于
2025年2月25日
许可协议