论如何用七天的时间打造一款(并不)爆款的匿名树洞网站

论如何用七天的时间打造一款(并不)爆款的匿名树洞网站

人一旦闲下来,是十分可怕的,就比如我,自从上了大学,每年国庆都能整出点骚活来:去年国庆,用 Jetpack Compose 搓了一个课程表 Android App,而到了今年,我直接搓了一个网站前后端出来……

起因

其实很早以前我就想开发一套面向我校学生的匿名树洞网站了,早在半个月前,我就已经开始研究如何将自己的服务接入学校的 CAS 统一认证系统里,正好十一闲着没事儿干,遂说干就干,就开始了开发。

开发过程

开发框架选型

因为先前有过相关的学习和开发经验,因此我毫不犹豫地选择了前后端分离的开发模式:前端采用 Vue 3 作为 JavaScript 框架,Vuetify 作为 UI 框架;后端采用 Java 开发 Spring Boot 框架应用。

虽然说是毫不犹豫,但其实前端和后端选型的时候,我还是有一些调整和妥协。前端方面,其实直到现在,Vuetify 的 Vue 3 适配版本 Vuetify Titan 仍处于 Beta Live 状态,RC 版本可能仍需要几个月的时间才会产生,但是因为 Vuetify 提供的组件和其他 API 相比其他 UI 框架实在不知道高到哪里去了,又因为个人也非常喜欢 Material Design,遂仍旧采用了 Vuetify。

而后端方面,作为一个 Kotlin 爱好者,刚开始我其实是打算用 Kotlin 开发后端的,但是又考虑到这套代码可能可以供学校的学生在入门 Java 或是 Spring Boot 开发的时候能作为参考学习(当然,这是我的一厢情愿),遂决定改用 Java 开发。

接下来,让我们谈谈详细的实际开发内容部分:

开发内容(前端)

先来谈谈前端。前端开发上,我采用了 vite 作为构建工具,使用 yarn 作为包管理器,除了 vue 和 vuetify 以外,我还主要引入了这些依赖:

  • vue-router(Vue 官方开发的路由系统)
  • vue-showdown(一套对 Markdown 解析库 showdown.js 的 Vue 封装)
  • typescript(由于 Vuetify 的引导式命令行新建项目向导默认初始化的项目没有 typescript,因此我手动引入了,但是不知道是不是我的配置问题,这导致 IDE 导入在 ts 文件中声明的函数时,导入的文件雷静总是错误的变为 js 而不是 ts)

我想得到的一个成品是:

  • 一个主页,可以以卡片流的方式显示最新的树洞(帖子)预览
  • 一个详细页,可以显示详细的树洞内容和评论
  • 一个发布树洞界面,可以输入树洞内容,选择标签
  • 一个回复树洞界面,可以回复指定的树洞
  • 一个登录界面,可以通过学校的 CAS 统一认证系统登录

最后,我大差不差的把这些页面的原型都开发了出来,在后端开发完成后,我又成功完成了与后端的对接,不过,与期望不同的是一些小问题导致的差异:

  1. 本来想做一个收藏功能,但是懒得做(即使后端已经声明好了对应的数据结构),所以没做
  2. 举报功能也没做
  3. 回复功能本来是想允许分别对主帖和评论回复的,但是最后没想好怎么表现,所以只做了对主贴回复
  4. 使用 CAS 统一认证系统登录的方案废了,因为学校的 CAS 统一认证系统的服务校验路由(/p3/serviceVaildate)无法通过外网访问,必须通过校园网(很显然我没有)或是 WebVPN 访问。本来我已经设计了一套通过要求用户提交 WebVPN Cookies 并且及时验证有效性后即可登录的模式,结果在线上测试的时候才发现这个 Cookies 只要换了个 IP 地址就会自动失效,因此使用用户提交的 Cookie 是不可能的,遂只能放弃,并改用一套通过向教育邮箱发送验证码来验证身份的注册方式。

开发前端期间,还遇到了许多疑难问题,比如组件中使用 this 作用域在开发环境可以工作,但是在生产环境无法工作的问题,又比如 Vue 3 新的组合式 API 和 setup 函数与先前使用方式不同导致差异的问题,又比如使用异步 fetch API 的问题。不过好在这些问题最后都有惊无险的化解了。不过在这里,必须特别感谢 GitHub 上 这位老兄的 Gist 提供了一套在 Vue 上使用异步 computed 属性的方式,简直是救了我的命(我在这个一年前的 Gist 下面回复,作者竟然还回我了,在交谈中,他建议我在现在最好使用 VueUse 提供的 computedAsync 功能,不过因为我懒得调整了所以最后没用)。

开发内容(后端)

前端部分的原型和主要框架开发大概花费了 5 天时间(9/30/2022 —— 10/4/2022),之后,我便开始着手开发后端。比起略显生涩的前端,早已驾轻就熟的后端才是我的大本营,因此开发时间也很快。

后端主要引入的开发依赖有:

  • org.springframework.boot:spring-boot-starter-data-jpa, org.springframework.boot:spring-boot-starter-data-jdbc, mysql:mysql-connector-java ORM,数据库连接桥和数据库驱动;
  • org.springframework.boot:spring-boot-starter-web Spring Boot Web 开发 Starter;
  • org.springframework.boot:spring-boot-starter-cache, org.springframework.boot:spring-boot-starter-data-redis, org.springframework.session:spring-session-data-redis, Spring Boot 数据和会话 Redis 缓存 Starter;
  • org.springframework.boot:spring-boot-starter-mail Spring Boot 邮件管理 Starter
  • cn.dev33:sa-token-spring-boot-starter SA Token 的 Spring Boot Starter 封装
  • com.google.code.gson:gson Google 的 Json 解析库
  • com.squareup.okhttp3:okhttp 一个 Kotlin 开发的 HTTP 客户端
  • com.fasterxml.jackson.dataformat:jackson-dataformat-xml Jackson 的 XML 解析模块(引入这个本来是为了识别 CAS 统一认证系统返回的 XML 信息)
  • cn.hutool:hutool-all 一个功能及其丰富和强大的 Utils 库
  • com.ramostear:Happy-Captcha 一个使用简单,功能强大的验证码模块
  • org.projectlombok:lombok Lombok(其实我是不想用 Lombok 的,但是奈何 Getter 和 Setter 太多了看得我眼花缭乱,不得已还是得把 Lombok 请回来)

采用了这些数据结构:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "users")
public class UserEntity {
@Id
    @Column(nullable = false)
    private long id;

    private String email;

    private String password;

    @OneToMany(mappedBy = "poster")
    private List<PostEntity> createdPosts;

    @OneToMany(mappedBy = "poster")
    private List<CommentEntity> createdComments;

    @ManyToMany
    @JoinTable(name = "STARRED_POSTS")
    private List<PostEntity> starredPosts;


}
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "posts")
public class PostEntity {
    @Id
    @Column(nullable = false)
    @SequenceGenerator(name = "post_id_seq", sequenceName = "post_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_id_seq")
    private long id;

    @ManyToOne
    private UserEntity poster;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "post")
    private List<CommentEntity> comments;

    @ManyToMany(mappedBy = "starredPosts")
    private List<UserEntity> starredUsers;

    @Column(nullable = false)
    private Date postTime;

    @Column(nullable = false, length = 65535, columnDefinition = "Text")
    private String content;

    @ElementCollection
    private List<String> attributes;

    @ElementCollection
    private List<String> tags;

}
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "comments")
public class CommentEntity {
    @Id
    @Column(nullable = false)
    @SequenceGenerator(name = "comment_id_seq", sequenceName = "comment_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "comment_id_seq")
    private long id;

    @ManyToOne
    private UserEntity poster;

    @ManyToOne
    private PostEntity post;

    @Column(nullable = false)
    private Date postTime;

    @Column(nullable = false)
    private String content;

}

添加了这些 Controller:

  • AuthController 登录和注册相关接口
  • CaptchaController 验证码相关接口
  • PostController 树洞发布和回复相关接口
  • UserController 用户信息相关接口
  • WebVpnController WebVPN 相关接口(不过最后没用上)

这期间也遇到了一些新的坑:

  1. 引入 jackson-dataformat-xml 导致 RestController 默认返回 XML 数据而不是 Gson(通过在 Spring Application 配置文件设置 spring.mvc.converters.preferred-json-mapper,且在前端请求时显式指定 Content-Type 解决)
  2. 经典跨域问题(通过添加 @CrossOgirin 注解解决)

(另外,HuTool 真是太好用了,我已经无法想象没有 HuTool 的 Java 开发了)

成果展示

生产站点: XAUFEHole – 西财树洞 (minecraft.kim)

image-20221007223206167

image-20221007223230397

image-20221007223248369

image-20221007223304592

其实可能用手机看起来效果会更好些:

image-20221007223346646

最后

个人感觉还是做了个很棒的工作的,并且最后的效果也很符合我的预期(除了人流量以外)。

这些代码也开源到了 GitHub 上(还没来得及设定一个开源许可证),有兴趣的可以参考看看:

shaokeyibb/XAUFEHoleFrontend: 西财树洞前端程序,Made by Vue3 && Vuetify (github.com)

shaokeyibb/XAUFEHoleBackend: 西财树洞后端程序,Made by SpringBoot (github.com)

那么,就这样吧。

评论

  1. 自由
    iPhone AppleWebKit 605.1.15
    2月前
    2022-10-08 13:53:57

    贺兰真的是越来越强了哈

  2. Yui
    Windows Edge 106.0.1370.34
    2月前
    2022-10-08 16:40:52

    其实不如试试 Kotlin (逃

    • 博主
      Yui
      Windows Edge 106.0.1370.34
      2月前
      2022-10-08 17:52:34

      我也知道Kotlin好啊😭😭😭😭

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇