docs(labs): добавлено наблюдение за выручкой до закрытия дня

Зачем: связать опоздания и отмены с изменением уже рассчитанного отчета.
Что: лабораторная с сохранением состояний заказов, ссылки в индексе и программе; убраны повторы в описании раздела.
Проверка: восемь однодневных world_next_day и etl_pipeline на Airflow 3.3.0; изменения сумм сошлись до копейки, закрытый день неизменен, DQ пройдены. Проверены 54 локальные ссылки, SQL-примеры и git diff --check.
This commit is contained in:
ddadmin committed 2026-09-22 03:58:42 -04:00
1 parent 2502277af1
commit 515f386e5f
3 files changed
+158 -29

No files matched your search

+13 -2
View File
@@ -124,8 +124,19 @@ DM](../architecture/dm.md).
### Почему выручка не сходится
Открывается после ступени 1. Расхождения между источниками, повторы
покупок и день, после которого отчет можно считать окончательным.
Открывается после ступени 1. Четыре расследования:
- [Куда пропали покупки](optional/missing-purchases.md): найти заказы без
события в трекере и объяснить разрыв воронки.
- [Почему повтор покупки завышает выручку](optional/duplicate-purchases.md):
посчитать деньги по номеру заказа и объяснить вклад повторов.
- [Какая позиция объясняет разницу в сумме](optional/order-amount-difference.md):
сопоставить корзины и найти причину расхождения до копейки.
- [Почему вчерашняя выручка меняется сегодня](optional/revenue-over-time.md):
проследить опоздания и отмены, затем определить момент закрытия дня.
Первые три работы используют готовые данные. Четвертая продвигает мир через
Airflow; для нее вы сохраняете состояния заказов после каждого модельного дня.
### Тонкий анализ данных
+131
View File
@@ -0,0 +1,131 @@
# Почему вчерашняя выручка меняется сегодня
**Чему учит:** объяснять изменения уже рассчитанного отчета поступлением
заказов и сменой их статусов, определять момент закрытия дня и границы
доступной истории.
Факультатив доступен после ступени 1. Понадобятся Airflow и DBeaver с
подключением `analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver).
Вы будете наблюдать один день оформления заказов, продвигая мир по одному
модельному дню. Новые данные останутся в стенде.
## Подготовьте первое наблюдение
Поставьте `world_live` на паузу. Дождитесь завершения начатых прогонов
генерации и обработки, затем запустите `etl_pipeline` и дождитесь успеха.
Так исходное наблюдение будет снято после обновления витрин.
Узнайте последний день событий и дату последнего полученного слепка:
```sql
SELECT
(SELECT max(event_date) FROM dds.event_v) AS event_day,
(SELECT max(snapshot_date) FROM ods.order_v) AS snapshot_day;
```
День `event_day` обозначим **D**. Наблюдайте именно его до конца работы.
Дата слепка, по которому сделано очередное наблюдение, будет **S**.
Это модельные даты магазина. Календарная дата запуска Airflow день D не задает.
Слепок снимается к концу суток и доставляется следующим прогоном. Поэтому
покупки последнего сыгранного дня уже видны в событиях, а их заказов в
хранилище еще нет. Проверьте это для D в `dds.order_v` и
`dm.purchase_vs_orders_v`.
## Подготовьте запросы и сохраняйте результаты
Напишите запросы для следующих наблюдений:
| Что сохранить | Откуда взять |
|---|---|
| Дату полученного слепка S | `max(snapshot_date)` из `ods.order_v` |
| Номер, статус и `items_total` каждого заказа дня D | `dds.order_v`, отбор по `order_date` |
| Число заказов в каждом статусе и сумму `items_total` только у `paid` | те же заказы дня D |
| Выручку дня D | сумма `revenue` из `dm.revenue_daily_v`, отбор по `report_date` |
| Номера покупок, еще ожидающих заказ | строки `awaiting_order` из `dm.purchase_vs_orders_v`, отбор по `order_day` |
Выручку складывайте по всем категориям. В этой витрине она означает стоимость
позиций оплаченных заказов, без скидки и доставки, поэтому должна сходиться
с суммой `items_total` у заказов в статусе `paid`.
Сохраняйте результаты запросов отдельными файлами, например выгрузками CSV
из DBeaver. Подписывайте D и S. До поступления первого слепка список заказов
пуст, а выручка равна нулю; сохраните и это наблюдение.
Публичное представление заказа показывает текущее состояние, а сырые слепки
хранятся ограниченное время. Сохраненные выгрузки — история для расследования.
## Продвигайте мир по одному дню
Для каждого следующего наблюдения:
1. Запустите `world_next_day` с `days = 1` и дождитесь успеха.
2. Запустите `etl_pipeline` и дождитесь успеха: `world_next_day` сам витрины
не обновляет.
3. Повторите свои запросы и сохраните новый комплект результатов с датой S.
Во всех запросах остается прежний день D. За один запуск проигрывайте один
день, чтобы сохранить промежуточные состояния. Пакетный `world_next_day`
отправляет события без пауз; ждать 24 минуты живого дня здесь не нужно.
Продолжайте до слепка **D + 7** включительно. У заказов дня D окно состоит
из семи дней, от D до D + 6. Слепок D + 6 содержит окончательное состояние,
а еще один шаг позволит проверить, что оно сохранилось за пределами окна.
От исходного наблюдения потребуется восемь однодневных запусков.
## Объясните изменения отчета
После каждого шага сравнивайте заказы по `order_id`, а не по положению строк
в выгрузке. Найдите:
- заказы, отсутствовавшие в первом слепке D, но появившиеся в следующем;
- переходы из `created` в `paid`;
- отмены заказов, для которых раньше был виден статус `paid`.
Первый слепок, полученный на следующий день, — обычная доставка заказов.
Опоздавшими здесь считаются те, которых не было уже в этом слепке.
Покажите на нескольких номерах, как они исчезают из `awaiting_order`.
Для двух соседних наблюдений разложите изменение выручки по заказам.
Вклад заказа — его `items_total`, если статус `paid`, и ноль в остальных
случаях, включая отсутствие заказа. Разность вкладов объясняет, сколько
каждый заказ добавил к отчету или убрал из него. Сумма этих изменений должна
совпасть с изменением `revenue` до копейки.
Запишите даты, между которыми выручка выросла или уменьшилась. Отдельный
отмененный заказ мог убрать деньги, даже если итог дня вырос: покажите это
по его вкладу. Одна общая сумма скрывает встречные изменения.
## Что известно об отменах
Возьмите отмененный заказ, у которого в вашей истории был статус `paid`.
Укажите два наблюдения, доказывающих, что он был оплачен, а затем отменен.
Для отмененного заказа без наблюдаемой оплаты сформулируйте предел вывода.
Дневные слепки могли не застать промежуточное состояние: оплата и отмена
могли произойти между ними. Поэтому отсутствие `paid` в выгрузках не
доказывает, что оплаты не было. Не заменяйте недостающие данные догадкой.
## Когда день закрыт
Сравните сохраненные состояния при S = D + 6 и S = D + 7: состав заказов,
их статусы, суммы и выручку. Объясните, почему отчет за D теперь окончательный.
Основание — получен и обработан последний слепок окна изменяемости;
одно совпадение сумм в соседних наблюдениях этого не доказывает.
После собственных выводов сверьтесь с описанием
[слепка и его доставки](../../architecture/orders/snapshot.md#слепок-и-его-доставка)
и [границ истории отмен](../../architecture/orders/fate.md#судьба-заказа).
## Что должно получиться
- [ ] сохранена последовательность наблюдений одного дня D с датами слепков S;
- [ ] найдены опоздавшие заказы и изменения статусов;
- [ ] изменение выручки между двумя наблюдениями объяснено вкладами заказов;
- [ ] оплата одной из последующих отмен подтверждена двумя наблюдениями;
- [ ] вы различаете доказанную оплату и отсутствие сведений о ней;
- [ ] состояние дня за пределами окна совпало с окончательным, а момент
закрытия обоснован правилом доставки слепков.
После работы оставьте `world_live` на паузе либо верните автоматическое
продвижение, если пользовались им до наблюдения.
+14 -27
View File
@@ -161,34 +161,21 @@
изменение выручки и история отмен используют одну историю заказов, поэтому
для них выбрана общая работа вместо трех отдельных.
- **Куда пропали покупки (#186).** Учащийся сравнивает подтверждения и события
покупки, находит заказы без события и объясняет разрыв воронки конкретными
заказами.
- **Почему повтор покупки завышает выручку (#187).** Движок убирает технический
дубль строки, а повтор одной покупки приходится находить по деловому ключу.
Учащийся сравнивает сумму событий с суммой по уникальным покупкам и
подтверждает, что разница равна вкладу повторов. Другие причины расхождения
с выручкой магазина при этом остаются.
- **Какая позиция объясняет разницу в сумме.** Учащийся сопоставляет корзину
из сырого `ecommerce` с позициями заказа, находит удаленный товар и объясняет
разницу до копейки. Для точных цен нужен исходный JSON: в готовых массивах
событий цены округлены до рублей.
- **Почему вчерашняя выручка меняется сегодня (#188).** Учащийся наблюдает один
день по мере поступления слепков: появляются опоздавшие заказы, меняются
статусы и выручка. По сохраненным состояниям объясняет причины изменений,
определяет момент закрытия дня и отделяет доказанную оплату отмененного
заказа от случаев, в которых историю оплаты восстановить нельзя.
- **[Куда пропали покупки](../labs/optional/missing-purchases.md) (#186).**
Учащийся объясняет разрыв воронки заказами, для которых нет события покупки.
- **[Почему повтор покупки завышает выручку](../labs/optional/duplicate-purchases.md) (#187).**
Разность клиентских сумм объясняется повторными событиями одной покупки.
- **[Какая позиция объясняет разницу в сумме](../labs/optional/order-amount-difference.md).**
Сравнение позиций из исходного JSON и заказа объясняет расхождение до копейки.
- **[Почему вчерашняя выручка меняется сегодня](../labs/optional/revenue-over-time.md) (#188).**
История одного дня связывает опоздания, оплату и отмены с изменением выручки,
позволяет определить момент закрытия дня и границу знания об оплате.
В четвертой лабораторной учащийся сам собирает историю одного дня оформления
заказов. Перед наблюдением он ставит `world_live` на паузу и дожидается
завершения начатых прогонов. Затем продвигает
мир по одному модельному дню через `world_next_day` и после каждого запуска
выполняет `etl_pipeline`. Только после завершения конвейера сохраняет дату
слепка, состояния отдельных заказов и выручку выбранного дня. Эти результаты
позволяют сравнить наблюдения даже после удаления сырых слепков. Готовые
слепки и сочетание готового примера с собственным наблюдением отклонены:
учащийся может получить нужную историю на стенде и сам увидеть, как новые
данные меняют уже рассчитанный отчет.
В четвертой лабораторной выбрано собственное наблюдение: учащийся продвигает
мир по одному дню через Airflow и сохраняет результаты после обновления витрин.
Готовые слепки и сочетание готового примера с собственным наблюдением
отклонены: нужную историю можно получить на стенде и самому увидеть,
как новые данные меняют уже рассчитанный отчет.
День считается закрытым после получения и обработки последнего слепка
семидневного окна изменяемости; этот слепок доставляется следующим модельным