---
url: https://docs.nemo12.com/architecture/sdd-053-photo-library-placements.md
description: >-
  Bản nháp kho ảnh chung (SDD-053): tag, phân phối ảnh theo chỗ hiển thị, hồ sơ
  mentor có chứng chỉ, thành tích, báo chí; mười quyết định.
---

# SDD-053 - Kho ảnh chung, tag, phân phối ảnh, hồ sơ mentor

Chỉ đạo 02.10.2026: trong dolphin cần chỗ quản lý thông tin mentor (xem chi tiết từng người), đưa
ảnh chân dung lên (ngang, dọc, vuông, nền đậm, nền sáng, ảnh trong lớp, ảnh với học viên), dùng tag
để phân loại theo vài cụm, và một khu riêng để **phân phối ảnh** lên các trang cụ thể. Album hiện có
phải có cùng các cơ chế ấy. Bổ sung 03.10.2026: mỗi mentor có chứng chỉ, thành tích thi, xuất hiện
trên báo chí.

Tài liệu này mở rộng [SDD-019](sdd-019-mentor-albums.md) (hồ sơ mentor, album) và
[SDD-009](sdd-009-media.md) (Media System). Nó **chưa được hiện thực**: đây là thiết kế đã
chốt, kèm lộ trình bốn giai đoạn ở §9.

## 1. Hiện trạng đo được (production, 03.10.2026)

| Đối tượng | Số liệu | Hệ quả |
| --- | --- | --- |
| Ảnh trong `media` | 73, trong đó 64 công khai | |
| Ảnh không có kích thước | **50 / 73** | Không biết ảnh ngang hay dọc, nên không lọc được theo hình dạng |
| Ảnh trong album | 62, ở 7 album (3 đã đăng) | |
| Ảnh album chưa có caption | 31 / 62 | |
| Ảnh album chưa có mô tả | 48 / 62 | Alt text tự sinh chỉ còn tên album để dựa vào |
| Ảnh album chưa có thời điểm chụp | 36 / 62 | |
| Alt text còn là tên tệp | 1 / 62 | |
| Hồ sơ mentor | 7, 6 đã đăng | |
| Mentor chưa có ảnh chân dung | **7 / 7** | Mọi thẻ mentor trên nemo12.com đang hiện chữ cái đầu thay cho ảnh |
| Mentor đã kiểm chứng là cựu học sinh trường chuyên | 5 / 7 | |

**Lượt audit đầu tiên (03.10.2026, skill `audit-photos`):**

* Đo và điền kích thước cho **45 / 45** ảnh công khai còn thiếu: 29 ngang, 14 dọc, 2 vuông; nền 20
  tối, 10 sáng, 15 ở vùng giữa (không đoán). Năm ảnh nội bộ (ảnh gia đình) bỏ qua có chủ đích.
  Hình dạng và nền chưa có cột nên mới chỉ nằm trong báo cáo, chờ giai đoạn 1.
* Dựng lại alt text cho 1 ảnh còn là tên tệp (`Screenshot 2026-07-17 at 23.25.22`, album "ABC").
* **Còn chờ người:** 7 / 7 mentor chưa có ảnh chân dung; ảnh album thiếu caption 31, thiếu mô tả 48,
  thiếu thời điểm chụp 36; caption "ấdas d" ở album "ABC" trông như chữ gõ thử.
* **Rủi ro cần chủ dự án quyết:** ba album **đã đăng** có tên học viên hoặc là lớp học sinh ("Trí
  Minh (Nemo AI)", "Đàm Anh Tú (Nemo AI)", "Lớp AI Teen 2026", tổng 36 ảnh) trong khi hệ thống **chưa
  có chỗ ghi đồng ý của phụ huynh** (§6, giai đoạn 1). Skill không tự gỡ đăng: đó là quyết định của
  người, đúng lớp ba của §8.

Mô hình hiện tại: mỗi mentor đúng **một** ô ảnh (`mentor_profiles.photo_media_id`); ảnh album gắn
người ở mức **album** (`photo_album_people`) chứ không ở mức từng ảnh; ảnh chưa có tag; chưa có
khái niệm "chỗ hiển thị". Tiền lệ phân phối duy nhất là `testimonials.placements_json` (SRC-676),
một mảng tên trang tự do, không ràng buộc.

## 2. Mười quyết định đã chốt

Chủ dự án chốt cả mười theo đề xuất (03.10.2026).

| # | Câu hỏi | Quyết định | Lý do |
| --- | --- | --- | --- |
| Q1 | Một kho ảnh hay hai | **Một kho chung.** Album chỉ là một cách nhóm ảnh | Tách hai kho thì cùng một tấm ảnh phải tải lên và điền thông tin hai lần |
| Q2 | Các cụm tag | **Năm cụm, danh sách đóng** (§3) | Tag tự do sinh ra "lớp học", "lop hoc", "classroom" cho cùng một nghĩa, và bộ lọc mất tác dụng |
| Q3 | Nền sáng hay tối | **Máy đoán lúc tải lên, người sửa được** | Đoán theo độ sáng đúng phần lớn trường hợp, sai với ảnh ngược sáng |
| Q4 | Phân phối theo chỗ | **Mỗi chỗ hiển thị tự khai ràng buộc**, khu phân phối chỉ gợi ý ảnh hợp | Cách duy nhất để không đặt ảnh ngang vào ô dọc |
| Q5 | Cố định hay xoay vòng | **Cố định, có thứ tự** | Xoay vòng làm khó kiểm tra trang đang hiện gì, và hỏng SEO ảnh |
| Q6 | Ảnh có học viên | **Bắt buộc có đồng ý của phụ huynh, ghi ở từng ảnh**; hệ thống chặn phân phối ra trang công khai khi thiếu | Ảnh trẻ em; tiền lệ testimonials đã chặn đăng khi thiếu consent |
| Q7 | Chứng chỉ, thành tích, báo chí | **Mục có cấu trúc**: tên, năm, đơn vị cấp hoặc tên báo, link nguồn, ảnh, trạng thái "đã kiểm chứng" | Giống danh sách cựu học sinh tiêu biểu: phải kiểm chứng được trước khi đưa lên |
| Q8 | Ảnh chứng chỉ và ảnh báo | **Cùng kho ảnh**, tag bối cảnh `certificate` / `press` | Một bài báo về một mentor hiện được cả ở trang mentor lẫn trang trường |
| Q9 | Ai làm gì | Mentor **tự tải và sửa** hồ sơ, ảnh, thành tích của mình; **phân phối** và **đánh dấu đã kiểm chứng / đã có đồng ý** chỉ staff, admin | Người biết rõ nhất về mình là chính mentor; quyết định đưa gì ra công khai thì cần một người thứ hai |
| Q10 | Skill audit ảnh | **Tự điền phần máy đo được**, **gợi ý không tự ghi** phần cần phán đoán, **không bao giờ tự đánh dấu** đồng ý hay kiểm chứng | §8 |

## 3. Tag: năm cụm, danh sách đóng

Danh sách sẽ khai trong code (file `mediaTags.ts` trong module showcase, dự định, chưa dựng ở giai đoạn 1), không ở
D1: cùng lập luận với tag đóng của Dory (SDD-024) và đăng ký dashboard (SDD-037). Thêm một giá trị là
một dòng code có typecheck, không phải một dòng dữ liệu có thể trỏ sai.

| Cụm | Giá trị | Ai gắn |
| --- | --- | --- |
| `shape` (hình dạng) | `landscape` · `portrait` · `square` | **Máy**, từ width/height. Tỉ lệ trong khoảng 0,9 tới 1,1 là vuông |
| `tone` (nền) | `light` · `dark` · `cutout` (đã tách nền) | **Máy đoán**, người sửa; lưu kèm nguồn `auto` / `human` |
| `context` (bối cảnh) | `portrait` · `classroom` · `with_learners` · `event` · `outdoor` · `certificate` · `press` | Người gắn; AI gợi ý |
| `people` (ai trong ảnh) | danh sách mentor và học viên | Người gắn; không phải tag chữ mà là liên kết tới hồ sơ |
| `quality` (mức dùng được) | `homepage_ready` · `internal_only` · `needs_replacement` | Staff, admin |

`shape` và `tone` là cột trên `media` (một ảnh đúng một giá trị); `context` là nhiều giá trị nên ở
bảng `media_tags` (dự định, chưa dựng); `people` ở bảng `media_people` (dự định, chưa dựng).

**Tag `with_learners` và bảng người-trong-ảnh (dự định, chưa dựng) phải khớp nhau.** Ảnh có học viên trong bảng đó
thì máy tự thêm `with_learners`. Ngược lại, gắn `with_learners` mà không chỉ ra học viên nào thì vẫn
bị coi là ảnh có trẻ em và vẫn phải qua cổng đồng ý (§6): thiếu thông tin không được trở thành lối
thoát khỏi cổng.

## 4. Mô hình dữ liệu (đề xuất, giai đoạn 1-3)

```text
media                       + orientation, tone, tone_source, quality, has_learners,
                              learner_consent, consent_note, consent_by, consent_at
media_tags                  (media_id, tag)                       -- cụm context
media_people                (media_id, person_type, person_id)    -- mentor | learner
media_placements            (slot_id, media_id, position, placed_by, placed_at)
mentor_credentials          (id, mentor_id, kind, title, issuer, year, source_url,
                             media_id, verified, verified_by, verified_at,
                             display_order, status)
```

* `photo_album_items` **giữ nguyên** và tiếp tục trỏ `media_id`. Tag, người trong ảnh, đồng ý đều
  nằm ở `media`, nên một ảnh album tự có cùng cơ chế với ảnh mentor (yêu cầu "album cũng có các cơ
  chế tương tự"). Caption theo vùng đọc và mô tả vẫn ở `photo_album_items` vì chúng thuộc về cách
  một album kể chuyện, không thuộc về tấm ảnh.
* `photo_album_people` (mức album) giữ lại cho tương thích; trang mới đọc người trong ảnh ở mức
  từng ảnh.
* `mentor_profiles.photo_media_id` **giữ lại** làm ảnh chân dung mặc định của thẻ mentor. Mọi ảnh
  khác của mentor là ảnh trong kho có `media_people` trỏ tới mentor đó.
* `mentor_credentials.kind` ∈ `certificate` · `competition` · `press`. Danh sách đóng kiểm ở API.

## 5. Chỗ hiển thị (placement slot): khai trong code, kèm ràng buộc

Mỗi chỗ hiển thị trên nemo12.com là một dòng trong đăng ký `placementSlots.ts`:

```ts
{
  id: "mentors.card",            // khoá ổn định
  page: "/mentors",
  label: "Ảnh trên thẻ mentor",
  capacity: 1,                   // mỗi mentor một ảnh
  perMentor: true,
  require: { shape: ["portrait", "square"], context: ["portrait"], quality: ["homepage_ready"] },
  minWidth: 600,
}
```

Danh sách khởi đầu (các chỗ đang dùng ảnh hôm nay):

| Slot | Trang | Số ảnh | Ràng buộc chính |
| --- | --- | --- | --- |
| `mentors.card` | `/mentors` | 1 mỗi mentor | dọc hoặc vuông, bối cảnh chân dung |
| `mentor.hero` | `/mentors/:slug` | 1 mỗi mentor | ngang, rộng tối thiểu 1600px |
| `mentor.gallery` | `/mentors/:slug` | tới 8 mỗi mentor | bất kỳ; ảnh có học viên phải có đồng ý |
| `mentor.credentials` | `/mentors/:slug` | theo số thành tích đã kiểm chứng | bối cảnh `certificate` hoặc `press` |
| `parents.classroom` | `/for-parents` | tới 6 | bối cảnh `classroom` hoặc `with_learners` |
| `students.classroom` | `/for-students` | tới 6 | như trên |
| `community.strip` | `/community` | tới 12 | bất kỳ đã đủ chuẩn |
| `home.team` | `/` | tới 4 | chân dung, `homepage_ready` |

Vì đăng ký nằm trong code, thêm một chỗ hiển thị là thêm một dòng; khu phân phối và trang web đọc
cùng một đăng ký nên không lệch nhau (cùng lập luận SDD-037 §2.2).

**Phân phối là cố định và có thứ tự** (Q5): `media_placements.position` quyết định thứ tự hiện.
Khu phân phối cho kéo thả đổi thứ tự trong một slot.

## 6. Cổng trước khi lên trang công khai

Một ảnh chỉ được gắn vào một slot công khai khi đủ cả ba:

1. `visibility = 'public'` và `moderation_status = 'approved'` (cổng sẵn có của SDD-019 §2).
2. Thoả ràng buộc của slot (§5): sai hình dạng hay thiếu bối cảnh thì API từ chối, kèm lý do bằng chữ.
3. **Ảnh có học viên** (`has_learners = 1`, hoặc tag `with_learners`, hoặc có `media_people` kiểu
   learner) thì phải `learner_consent = 1`. Đồng ý ghi ở **từng ảnh** (Q6), kèm `consent_note`
   (kênh và ngày hỏi), và chỉ staff, admin đánh dấu được.

Một thành tích (`mentor_credentials`) chỉ hiện công khai khi `verified = 1` **và** có `source_url`.

API công khai đọc `media_placements` cho từng slot rồi lọc lại theo cổng này lúc trả về, chứ không
tin vào trạng thái lúc gắn: đồng ý có thể bị rút sau khi ảnh đã được phân phối.

## 7. Các khu trong dolphin

Đặt trong cụm "What the public sees" ở trang chủ (SRC-1176), trừ hồ sơ mentor ở cụm "People and
schools". Mọi khu theo luật chỉ đọc mặc định (SRC-1178): Edit mới sửa, Save/Cancel đứng đúng chỗ
nút Edit (SRC-1181).

| Khu | Đường dẫn | Việc |
| --- | --- | --- |
| Kho ảnh | `/photos` | Lưới ảnh, lọc theo năm cụm tag; badge "thiếu thông tin" |
| Chi tiết một ảnh | `/photos/:id` | Hai cột ảnh và thông tin; tag, người trong ảnh, đồng ý, nơi đang được phân phối |
| Thêm ảnh | `/photos/upload` | Trang riêng; máy đo hình dạng, nền, kích thước ngay khi tải |
| Album | `/albums/...` | Như hiện tại (SDD-019 §12-14), ảnh album mở ra chính trang chi tiết ảnh của kho |
| Phân phối | `/placements` | Danh sách slot; mỗi slot hiện ràng buộc, ảnh đang gắn theo thứ tự, và gợi ý ảnh hợp |
| Hồ sơ mentor | `/team/:id` | Thông tin, ảnh chân dung (chọn từ kho), ảnh khác của mentor, chứng chỉ, thành tích thi, báo chí |

## 8. Skill `audit-photos`

`.claude/skills/audit-photos/SKILL.md`, thuộc họ `audit-*` và được `/audit` gọi. Ba lớp hành động,
đúng Q10:

| Lớp | Ví dụ | Skill làm gì |
| --- | --- | --- |
| Máy đo được | kích thước, hình dạng, độ sáng nền, alt text còn là tên tệp | **Tự điền** qua `seed-data`, kèm SELECT kiểm chứng |
| Cần phán đoán | bối cảnh, ai trong ảnh, caption, mô tả | **Gợi ý**, ghi vào báo cáo; người duyệt rồi mới ghi |
| Cam kết với người ngoài | đồng ý của phụ huynh, thành tích đã kiểm chứng | **Không bao giờ tự đánh dấu**; chỉ báo thiếu |

## 9. Lộ trình

| Giai đoạn | Nội dung | Phụ thuộc |
| --- | --- | --- |
| 1 | Cột mới trên `media`, `media_tags`, `media_people`; máy đo hình dạng và nền lúc tải lên; khu Kho ảnh và Chi tiết ảnh | migration |
| 2 | `placementSlots.ts`, `media_placements`, cổng §6, khu Phân phối | 1 |
| 3 | `mentor_credentials`, hồ sơ mentor mở rộng, quyền mentor tự sửa (Q9) | 1 |
| 4 | nemo12.com đọc ảnh theo slot thay cho `photo_media_id` và album trực tiếp | 2, 3 |

Trước giai đoạn 1, skill `audit-photos` đã chạy được trên mô hình hiện tại (đo và điền kích thước,
sửa alt text tên tệp, báo phần thiếu); các mục của §3-§6 được báo là "chưa có mô hình".

## 10. Trace

| SRC | REQ | Hiện thực |
| --- | --- | --- |
| [SRC-1215](../intake.md) | REQ-MED-08, REQ-MED-09, REQ-MED-10, REQ-MED-11, REQ-MEN-20, REQ-MEN-21 | Thiết kế; hiện thực theo §9. Skill `.claude/skills/audit-photos/SKILL.md` |
