SnapDTU là gì
SnapDTU là một mạng xã hội chia sẻ ảnh theo ngày, dành riêng cho cộng đồng sinh viên Đại học Duy Tân (DTU), Đà Nẵng. Mỗi sinh viên đăng nhập bằng chính tài khoản myDTU của trường, nên lớp, khoa và lịch học được nhận diện tự động — cộng đồng trong app là 100% sinh viên DTU thật.
Triết lý sản phẩm xoay quanh sự chân thật và chọn lọc: mỗi người chỉ đăng tối đa 3 ảnh/ngày, và muốn xem ảnh của bạn bè thì phải góp một khoảnh khắc của chính mình. Kết quả là một feed thật, gần gũi, theo từng lớp học và khuôn viên.
SnapDTU không dựng trên template hay nền tảng dựng-app có sẵn. Toàn bộ logic nghiệp vụ — tích hợp đăng nhập myDTU, cộng đồng theo môn học, cổng vị trí khuôn viên, thuật toán xếp hạng feed, đường ống kiểm duyệt ảnh tự động và bộ máy nhắc nhở theo lịch học — đều do đội ngũ tự thiết kế và lập trình. Hai ứng dụng native dùng chung một backend serverless tự vận hành. Các phần sau mô tả cụ thể từng thành phần đó.
Đặc tả tính năng
SnapDTU gồm các nhóm tính năng sau, mỗi tính năng kèm tham số nghiệp vụ thực tế đang chạy trong hệ thống.
Đăng nhập bằng myDTU (SSO trường)
Sinh viên đăng nhập bằng tài khoản myDTU. Mật khẩu chỉ dùng một lần để xác thực qua lớp proxy ở backend và không bao giờ được lưu; sau đó app hoạt động bằng phiên đăng nhập riêng của Firebase. Hồ sơ (mã sinh viên, họ tên, lớp, khoa, khoá) được đồng bộ tự động. Ngoài ra hỗ trợ liên kết Google và Apple, một tài khoản gắn nhiều phương thức.
Chụp & đăng ảnh hàng ngày
Chụp ảnh ngay trong app (chế độ chụp im lặng, không tiếng màn trập) và đăng với phạm vi tự chọn. Giới hạn 3 ảnh/ngày, làm mới lúc 0h00. Khi đăng vào lớp, ảnh được gắn cộng đồng môn học tương ứng.
Cổng "đăng để xem" (feed gate)
Chưa đăng ảnh hôm nay, sinh viên chỉ xem được 5 ảnh đầu trong feed; ảnh thứ 6 trở đi bị làm mờ kèm lời mời đăng ảnh. Chỉ cần đăng 1 ảnh là mở khoá xem không giới hạn đến hết ngày. Đây là cơ chế cốt lõi giữ feed luôn có sự tham gia hai chiều.
Cổng vị trí khuôn viên DTU
Khi đăng ảnh vào lớp, thiết bị phải nằm trong bán kính 500m quanh một trong các cơ sở của DTU. Hệ thống tự nhận cơ sở gần nhất; backend là nơi xác thực sau cùng và từ chối (403) nếu ngoài vùng. Ảnh hợp lệ được gắn toạ độ và mã cơ sở để hiển thị trên bản đồ hồ sơ.
Cộng đồng theo môn học
Lịch học (TKB) được đồng bộ trực tiếp từ myDTU. Mỗi môn được gom thành một cộng đồng theo khoá định danh ổn định {termId}-{mã môn}, nên mọi lớp của cùng một môn chia sẻ chung một feed. Thành viên được thêm tự động theo lịch học, không cần mời thủ công.
Thuật toán xếp hạng feed
Feed không xếp theo thời gian thuần. Mỗi bài có điểm feed_score tổng hợp từ ba yếu tố có trọng số, ưu tiên nội dung gần gũi và mới mẻ trong cộng đồng của bạn.
Kiểm duyệt ảnh tự động 3 tầng
Mọi ảnh được quét bằng Google Vision SafeSearch trước khi hiển thị. Kết quả phân theo ba tầng xử lý: chặn ngay và hạn chế người dùng, đưa vào hàng đợi duyệt thủ công, hoặc đăng tự động — toàn bộ có nhật ký kiểm toán.
Tương tác realtime & an toàn cộng đồng
Thả tim và bình luận đồng bộ realtime giữa các thiết bị (cập nhật optimistic ở client, đếm chính xác qua Cloud Functions). Bình luận được lọc từ ngữ không phù hợp ở cả client và server. Người dùng có thể báo cáo hoặc chặn người khác; nội dung bị nhiều báo cáo sẽ vào hàng đợi duyệt.
Nhắc nhở & chủ đề mỗi ngày
Mỗi ngày hệ thống đăng một "chủ đề hôm nay" và gửi push nhắc đăng ảnh vào các khung giờ trong ngày. Bộ máy nhắc nhở chạy trên Cloudflare (cron + D1 + queues), prefetch danh sách người dùng từ tối hôm trước rồi gửi theo lô qua FCM/APNs. Người dùng có thể tắt nhắc nhở trong Cài đặt.
Hồ sơ: timeline, streak, kỷ yếu, bản đồ ảnh
Trang cá nhân tổng hợp ảnh theo dòng thời gian từng tháng, chuỗi ngày đăng liên tiếp (streak), lưới "kỷ yếu" ảnh và bản đồ những nơi đã chụp gần các cơ sở DTU. Ảnh cá nhân (không đăng vào lớp) được lưu kho riêng, không vào feed công khai.
Bảng quản trị (admin console)
Console web riêng cho vận hành: duyệt hàng đợi kiểm duyệt, quản lý hạn chế người dùng, xem nhật ký kiểm duyệt và cấu hình runtime (ví dụ số ảnh tối đa/ngày, bật/tắt cổng vị trí) mà không cần phát hành lại app.
Quy trình người dùng
Bốn luồng nghiệp vụ chính, từ tương tác của người dùng đến xử lý ở backend.
- Nhập tài khoản myDTUSinh viên nhập tài khoản trường trên màn đăng nhập (iOS/Android).
- App gửi lên backend qua kênh mã hoáThông tin được gửi tới lớp proxy ở Cloudflare Workers, không gửi thẳng từ client tới đâu khác.
- Backend xác thực với hệ thống trườngWorkers xác thực với myDTU, nhận hồ sơ sinh viên rồi loại bỏ mật khẩu — không lưu ở bất kỳ đâu.
- Tạo custom token FirebaseBackend ký một custom token và đồng bộ hồ sơ vào Firestore (
users/{uid}). - App đổi token lấy phiên đăng nhậpClient gọi
signIn(withCustomToken:); từ đây mọi truy cập dùng phiên Firebase, tự gia hạn 30 ngày. - Đồng bộ lịch học & hoàn tấtApp tải TKB và hồ sơ, vào màn chính.
- Chụp & chọn phạm viNgười dùng chụp ảnh và chọn scope: lớp, khoa hay trường.
- Tải lên backendẢnh nén được gửi tới Workers kèm token và metadata (scope, mã môn, toạ độ nếu có).
- Backend kiểm tra điều kiệnXác thực token, kiểm tra hạn mức 3 ảnh/ngày, và cổng vị trí 500m nếu đăng vào lớp.
- Lưu vào kho chờ duyệt (R2)Ảnh được lưu vào bucket R2 tạm; tạo bản ghi bài viết trạng thái
processing. - Hàng đợi kiểm duyệtSự kiện đẩy vào Cloudflare Queues; consumer gọi Google Vision SafeSearch.
- Quyết định 3 tầngChặn (vi phạm nặng → xoá + hạn chế 24h), chờ duyệt (đưa vào hàng đợi thủ công), hoặc đăng (chuyển sang bucket công khai).
- App cập nhật realtimeClient lắng nghe trạng thái bài viết và hiển thị kết quả tức thì (đã đăng / đang xét / bị từ chối).
- Tải feedClient truy vấn các bài
published, sắp theofeed_scoregiảm dần, 20 bài/trang. - Đếm lượt xemNếu hôm nay chưa đăng, mỗi ảnh xem qua được đếm vào hạn mức xem miễn phí.
- Chạm cổng ở ảnh thứ 6Ảnh từ thứ 6 bị làm mờ kèm CTA "Đăng 1 ảnh để xem không giới hạn".
- Người dùng đăng ảnhSau khi đăng thành công, cờ "đã đăng hôm nay" bật lên.
- Mở khoá xem vô hạnFeed dựng lại, gỡ cổng; tự prefetch trang tiếp khi gần hết.
- Đăng chủ đề trong ngàyCloud Function tạo "chủ đề hôm nay" lúc nửa đêm (giờ VN).
- Prefetch tối hôm trướcCron quét người dùng bật nhắc nhở, đẩy vào queue, lấy sẵn FCM token vào D1.
- Cron các khung giờĐến các mốc trong ngày, job gửi đọc danh sách đã prefetch từ D1.
- Gửi theo lô qua FCM/APNsWorker gửi push hàng loạt; iOS qua APNs, Android qua FCM, có gộp thông báo chống spam.
- Người dùng nhận & mở appChạm thông báo mở thẳng màn chụp/đúng bài liên quan.
Kiến trúc kỹ thuật
SnapDTU theo mô hình native client + backend serverless ở biên (edge). Client không gọi thẳng tới dịch vụ bên thứ ba nhạy cảm; mọi nghiệp vụ nhạy cảm đi qua lớp Cloudflare Workers đặt gần người dùng.
Sơ đồ hệ thống
Ngăn xếp công nghệ
| Lớp | Công nghệ | Vai trò |
|---|---|---|
| iOS | Swift + SwiftUI (iOS 17+), Kingfisher, Firebase SDK | Ứng dụng native iPhone |
| Android | Kotlin + Jetpack Compose Material3 (minSdk 26), Retrofit, Firebase SDK | Ứng dụng native Android |
| HTTP edge | Cloudflare Workers (TypeScript, Hono) | Auth proxy, upload, đồng bộ lịch, kiểm duyệt, admin API |
| Trigger & cron | Firebase Cloud Functions (TypeScript) | Đếm tim/bình luận, thông báo, chủ đề hằng ngày |
| Cơ sở dữ liệu | Firebase Firestore | Hồ sơ, bài viết, môn học, hàng đợi duyệt — realtime listeners |
| Lưu trữ ảnh | Cloudflare R2 | Bucket chờ duyệt → bucket công khai (zero egress) |
| Kiểm duyệt | Google Vision SafeSearch | Phân loại nội dung ảnh tự động |
| Push | FCM + APNs | Thông báo tim/bình luận, nhắc nhở |
| Lịch nhắc | Cloudflare D1 + Queues + Cron | Prefetch & gửi nhắc nhở theo khung giờ |
Các luồng dữ liệu chính
- Xác thực: client → Workers (proxy myDTU) → tạo custom token → Firebase Auth → client lưu phiên an toàn.
- Đăng ảnh: client → Workers (kiểm hạn mức + vị trí) → R2 chờ duyệt → Queue → Vision → cập nhật Firestore → client nhận realtime.
- Thông báo: sự kiện (tim/bình luận/nhắc) → Functions/Workers → tra FCM token → gửi qua FCM/APNs → deep-link mở đúng màn.
- Kiểm duyệt: sự kiện R2 → Queue consumer → Vision SafeSearch → cây quyết định 3 tầng → cập nhật trạng thái + ghi nhật ký kiểm toán.
Năng lực tự xây dựng
Những thành phần dưới đây là logic nghiệp vụ riêng của SnapDTU, không có sẵn ở bất kỳ nền tảng dựng-app nào — đây là phần lõi tạo nên tính độc quyền của sản phẩm.
Tích hợp đăng nhập myDTU
Lớp xác thực riêng giúp sinh viên dùng tài khoản trường, ánh xạ lớp/khoa/khoá vào hồ sơ mà mật khẩu không bao giờ được lưu.
Cộng đồng theo môn học
Cơ chế định danh môn ổn định {termId}-{mã môn} gom mọi lớp của cùng một môn vào một cộng đồng, thành viên tự động theo TKB.
Cổng vị trí khuôn viên
Geofence 500m quanh 4 cơ sở DTU, xác thực ở backend bằng khoảng cách Haversine — gắn ảnh với khuôn viên thật.
Thuật toán feed_score
Công thức xếp hạng riêng cân bằng recency 40% / engagement 30% / proximity 30%, ưu tiên nội dung gần gũi trong cộng đồng.
Đường ống kiểm duyệt 3 tầng
Tự động hoá Google Vision với ngưỡng phân tầng riêng, hàng đợi duyệt thủ công và hạn chế người dùng luỹ tiến, có nhật ký kiểm toán.
Bộ máy nhắc nhở trên D1
Lịch nhắc đặt trên Cloudflare D1 với kiến trúc 2 pha prefetch + gửi theo lô, không phụ thuộc dịch vụ gửi tin bên thứ ba.
Hai app native, một backend
iOS và Android dùng chung một backend duy nhất — đồng bộ chéo realtime, không fork API, chi phí bảo trì tối thiểu.
Console quản trị cấu hình runtime
Bảng admin cho phép đổi tham số vận hành (số ảnh/ngày, cổng vị trí…) tức thời mà không cần phát hành lại app.
Bảng tham chiếu kỹ thuật
Tham số & hạn mức nghiệp vụ
| Tham số | Giá trị | Ghi chú |
|---|---|---|
| Ảnh tối đa/ngày | 3 (cấu hình runtime) | Reset 0h00; admin chỉnh được |
| Xem trước khi chưa đăng | 5 ảnh | Đăng 1 ảnh → xem vô hạn |
| Bán kính cổng vị trí | 500 mét | Quanh 4 cơ sở DTU |
| Trọng số feed_score | 40 / 30 / 30 | recency / engagement / proximity |
| Phân trang feed | 20 bài/trang | Prefetch khi còn ~3 bài |
| Độ dài bình luận | ≤ 500 ký tự | Lọc từ ngữ 2 lớp |
| Nhắc nhở/ngày | 4 mốc | Theo khung giờ trong ngày (giờ VN) |
| Hạn chế khi vi phạm nặng | 24 giờ (luỹ tiến) | Tái phạm tăng mức |
Nhóm endpoint backend (đại diện)
| Endpoint | Mục đích | Xác thực |
|---|---|---|
POST /auth/mydtu | Đổi tài khoản myDTU lấy phiên Firebase | Không (proxy) |
PUT /upload/image | Tải ảnh + metadata để kiểm duyệt | Token Firebase |
POST /schedule/sync | Đồng bộ TKB từ myDTU | Token Firebase |
GET /schedule/live | Lấy lịch học trực tiếp | Token Firebase |
GET /admin/* | Duyệt nội dung, cấu hình runtime | Token Firebase (admin) |
Bộ sưu tập Firestore chính
| Bộ sưu tập | Nội dung |
|---|---|
users | Hồ sơ sinh viên, hạn mức ngày, FCM token, tuỳ chọn thông báo, kho ảnh cá nhân con |
posts | Bài viết (scope, trạng thái, like/comment count, feed_score) + subcollection likes/comments/reports |
courses | Môn học + sessions (lịch) + members (thành viên) + posts (bài theo lớp) |
daily_prompts | Chủ đề ảnh theo từng ngày |
review_queue · moderation_log | Hàng đợi duyệt thủ công & nhật ký kiểm toán kiểm duyệt |