@taopao/memoir 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +8 -2
- package/assets/migrations-pg/0100_baseline.sql +1115 -0
- package/assets/migrations-pg/0101_functions.sql +93 -0
- package/assets/migrations-pg/0102_views.sql +2425 -0
- package/assets/migrations-pg/0103_constraints.sql +147 -0
- package/assets/migrations-pg/0104_protection.sql +33 -0
- package/assets/migrations-pg/0105_bocom_credit_card.sql +43 -0
- package/assets/migrations-pg/0106_psbc_debit_card.sql +87 -0
- package/assets/migrations-pg/0107_tailong_debit_card.sql +87 -0
- package/assets/migrations-pg/0108_ccb_debit_card.sql +87 -0
- package/assets/migrations-pg/0109_ccb_credit_card.sql +63 -0
- package/assets/migrations-pg/0110_icbc_debit_card.sql +87 -0
- package/assets/migrations-pg/0111_icbc_credit_card.sql +66 -0
- package/assets/migrations-pg/0112_boc_debit_card.sql +88 -0
- package/assets/migrations-pg/0113_jsb_debit_card.sql +88 -0
- package/assets/migrations-pg/0114_cmbc_debit_card.sql +88 -0
- package/assets/migrations-pg/0115_hzbank_debit_card.sql +88 -0
- package/assets/migrations-pg/0116_unified_bank_documents.sql +197 -0
- package/assets/migrations-pg/0117_agent_submissions.sql +50 -0
- package/assets/migrations-pg/0118_agent_reversions_analysis.sql +34 -0
- package/assets/migrations-pg/0119_agent_extension_registry.sql +72 -0
- package/assets/migrations-pg/0120_agent_record_sources.sql +22 -0
- package/assets/migrations-pg/0121_agent_extension_deprecation.sql +30 -0
- package/assets/migrations-pg/0122_agent_extension_controlled_revert.sql +107 -0
- package/assets/migrations-pg/0123_agent_extension_versions.sql +54 -0
- package/assets/migrations-pg/0124_agent_extension_retirement_groups.sql +26 -0
- package/assets/migrations-pg/0125_agent_candidate_reviews.sql +30 -0
- package/assets/migrations-pg/0126_agent_module_lifecycle.sql +93 -0
- package/assets/migrations-pg/0127_agent_tasks.sql +64 -0
- package/assets/migrations-pg/0128_agent_task_leases.sql +80 -0
- package/assets/migrations-pg/0129_agent_task_plans.sql +66 -0
- package/assets/migrations-pg/0130_agent_task_submissions.sql +72 -0
- package/assets/migrations-pg/0131_agent_task_completions.sql +93 -0
- package/assets/migrations-pg/0132_agent_task_changes.sql +113 -0
- package/assets/migrations-pg/0133_agent_imported_source_snapshots.sql +152 -0
- package/assets/migrations-pg/0134_agent_analysis_datasets.sql +6 -0
- package/assets/migrations-pg/0135_agent_maintenance_tasks.sql +139 -0
- package/assets/migrations-pg/0136_agent_task_structures.sql +66 -0
- package/assets/migrations-pg/0137_agent_personal_identity.sql +149 -0
- package/assets/migrations-pg/0138_agent_work_records.sql +24 -0
- package/assets/migrations-pg/0139_shared_files.sql +43 -0
- package/assets/migrations-pg/0140_shared_script_runs.sql +36 -0
- package/assets/migrations-pg/0141_payment_bank_links.sql +96 -0
- package/assets/migrations-pg/0142_consumption_order_types_and_places.sql +287 -0
- package/assets/migrations-pg/0143_skill_direct_write.sql +330 -0
- package/assets/migrations-pg/0144_skill_schema_archive.sql +73 -0
- package/assets/migrations-pg/0145_skill_ingest_wiki.sql +91 -0
- package/assets/migrations-pg/0146_skill_load.sql +17 -0
- package/assets/migrations-pg/0147_drop_agent_schema.sql +147 -0
- package/assets/migrations-pg/0148_ingest_lanes.sql +19 -0
- package/assets/migrations-pg/0149_account_ownership.sql +233 -0
- package/assets/migrations-pg/0150_bank_card_exact_link.sql +24 -0
- package/assets/migrations-pg/0151_account_ownership_enforce.sql +35 -0
- package/assets/migrations-pg/0152_account_ownership_views.sql +750 -0
- package/assets/migrations-pg/0153_web_frontend.sql +34 -0
- package/assets/migrations-pg/0154_audio_record_owners.sql +103 -0
- package/assets/migrations-pg/0155_ingest_knowledge_tables.sql +171 -0
- package/assets/migrations-pg/0156_batch_playbook_note.sql +17 -0
- package/assets/migrations-pg/0157_last_contact.sql +166 -0
- package/assets/migrations-pg/0158_refund_match_hash_join.sql +91 -0
- package/assets/migrations-pg/0159_fact_occurred_time.sql +88 -0
- package/assets/migrations-pg/0160_agent_notes.sql +118 -0
- package/assets/migrations-pg/0161_life_history.sql +314 -0
- package/assets/migrations-pg/0162_person_card.sql +427 -0
- package/assets/migrations-pg/0163_parking_drop_personal_defaults.sql +12 -0
- package/assets/native/darwin-arm64.node +0 -0
- package/assets/playbooks/audio.md +83 -0
- package/assets/playbooks/chat.md +37 -0
- package/assets/playbooks/common.md +66 -0
- package/assets/playbooks/intimate.md +19 -0
- package/assets/playbooks/journal.md +17 -0
- package/assets/playbooks/orders.md +15 -0
- package/assets/playbooks/payments.md +16 -0
- package/assets/playbooks/self.md +29 -0
- package/bin/install-codex-skill.mjs +77 -0
- package/bin/memoir.mjs +5 -0
- package/dist/cli.js +34062 -0
- package/package.json +40 -4
- package/release-manifest.json +432 -0
- package/skill/SKILL.md +120 -0
- package/skill/references//345/256/211/350/243/205/344/275/215/347/275/256.md +21 -0
- package/skill/references//346/225/260/346/215/256/345/217/243/345/276/204.md +36 -0
- package/skill/scripts/memoir.mjs +82 -0
|
@@ -0,0 +1,2425 @@
|
|
|
1
|
+
-- 视图金额沿用 SQLite 浮点比例;向零截断后转 BIGINT,ROUND 使用 numeric 保持半分舍入。
|
|
2
|
+
/* 视图保持原有返回列;CAST 浮点金额先向零截断,整数使用 BIGINT,ROUND 显式使用 numeric 保持半分舍入。 */
|
|
3
|
+
/* _v_annotations */
|
|
4
|
+
/* 视图保持原有返回列;方言差异在此显式处理。 */
|
|
5
|
+
/* _v_annotations */
|
|
6
|
+
CREATE VIEW _v_annotations AS
|
|
7
|
+
WITH active AS (
|
|
8
|
+
SELECT
|
|
9
|
+
*,
|
|
10
|
+
ROW_NUMBER() OVER (
|
|
11
|
+
PARTITION BY target_type, target_key, kind
|
|
12
|
+
ORDER BY CASE WHEN basis = 'manual' OR updated_by = 'me' THEN 0 ELSE 1 END NULLS FIRST, updated_at DESC NULLS LAST, id DESC NULLS LAST
|
|
13
|
+
) AS priority
|
|
14
|
+
FROM labels
|
|
15
|
+
WHERE
|
|
16
|
+
deleted_at IS NULL
|
|
17
|
+
)
|
|
18
|
+
SELECT
|
|
19
|
+
target_type,
|
|
20
|
+
target_key,
|
|
21
|
+
MAX(
|
|
22
|
+
CASE
|
|
23
|
+
WHEN kind = 'hidden' AND NOT LOWER("value") IN ('0', 'false', 'no', '否')
|
|
24
|
+
THEN 1
|
|
25
|
+
ELSE 0
|
|
26
|
+
END
|
|
27
|
+
) AS hidden,
|
|
28
|
+
MAX(CASE WHEN kind = 'category' AND priority = 1 THEN "value" END) AS category,
|
|
29
|
+
MAX(
|
|
30
|
+
CASE
|
|
31
|
+
WHEN kind = 'sensitivity'
|
|
32
|
+
AND priority = 1
|
|
33
|
+
AND "value" IN ('normal', 'private', 'intimate')
|
|
34
|
+
THEN "value"
|
|
35
|
+
END
|
|
36
|
+
) AS sensitivity
|
|
37
|
+
FROM active
|
|
38
|
+
GROUP BY
|
|
39
|
+
target_type,
|
|
40
|
+
target_key;
|
|
41
|
+
|
|
42
|
+
/* _v_beneficiary_groups */
|
|
43
|
+
/* _v_beneficiary_groups */
|
|
44
|
+
CREATE VIEW _v_beneficiary_groups AS
|
|
45
|
+
WITH active AS (
|
|
46
|
+
SELECT
|
|
47
|
+
b.*
|
|
48
|
+
FROM beneficiaries AS b
|
|
49
|
+
JOIN persons AS p
|
|
50
|
+
ON p.id = b.person_id
|
|
51
|
+
WHERE
|
|
52
|
+
b.deleted_at IS NULL AND p.deleted_at IS NULL
|
|
53
|
+
), shares AS (
|
|
54
|
+
SELECT
|
|
55
|
+
*,
|
|
56
|
+
CASE
|
|
57
|
+
WHEN NOT share_ratio IS NULL
|
|
58
|
+
THEN share_ratio
|
|
59
|
+
ELSE GREATEST(
|
|
60
|
+
0.0,
|
|
61
|
+
CAST(1.0 AS DOUBLE PRECISION) - SUM(COALESCE(share_ratio, 0)) OVER (PARTITION BY target_type, target_key)
|
|
62
|
+
) / NULLIF(
|
|
63
|
+
SUM(CAST(share_ratio IS NULL AS INT)) OVER (PARTITION BY target_type, target_key),
|
|
64
|
+
0
|
|
65
|
+
)
|
|
66
|
+
END AS effective_share
|
|
67
|
+
FROM active
|
|
68
|
+
)
|
|
69
|
+
SELECT
|
|
70
|
+
target_type,
|
|
71
|
+
target_key,
|
|
72
|
+
CAST(JSON_AGG(JSON_OBJECT('person_id': person_id, 'share_ratio': effective_share)) AS TEXT) AS beneficiaries_json
|
|
73
|
+
FROM (
|
|
74
|
+
SELECT
|
|
75
|
+
*
|
|
76
|
+
FROM shares
|
|
77
|
+
ORDER BY
|
|
78
|
+
person_id NULLS FIRST
|
|
79
|
+
)
|
|
80
|
+
GROUP BY
|
|
81
|
+
target_type,
|
|
82
|
+
target_key;
|
|
83
|
+
|
|
84
|
+
/* _v_date_occurrences */
|
|
85
|
+
/* _v_date_occurrences */
|
|
86
|
+
CREATE VIEW _v_date_occurrences AS
|
|
87
|
+
WITH RECURSIVE years(year) AS (
|
|
88
|
+
SELECT
|
|
89
|
+
CAST(SUBSTRING(VAULT_TODAY() FROM 1 FOR 4) AS BIGINT) - 1
|
|
90
|
+
UNION ALL
|
|
91
|
+
SELECT
|
|
92
|
+
year + 1
|
|
93
|
+
FROM years
|
|
94
|
+
WHERE
|
|
95
|
+
year < CAST(SUBSTRING(VAULT_TODAY() FROM 1 FOR 4) AS BIGINT) + 8
|
|
96
|
+
), dates AS (
|
|
97
|
+
SELECT
|
|
98
|
+
d.*
|
|
99
|
+
FROM important_dates AS d
|
|
100
|
+
LEFT JOIN _v_annotations AS a
|
|
101
|
+
ON a.target_type = 'important_dates' AND a.target_key = d.id
|
|
102
|
+
WHERE
|
|
103
|
+
d.deleted_at IS NULL AND d.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
104
|
+
)
|
|
105
|
+
SELECT
|
|
106
|
+
d.*,
|
|
107
|
+
VAULT_CALENDAR_DATE(d.calendar, d.date_value, y.year) AS occurs_on
|
|
108
|
+
FROM dates AS d
|
|
109
|
+
CROSS JOIN years AS y
|
|
110
|
+
WHERE
|
|
111
|
+
d.recurrence = 'yearly'
|
|
112
|
+
UNION ALL
|
|
113
|
+
SELECT
|
|
114
|
+
d.*,
|
|
115
|
+
COALESCE(
|
|
116
|
+
VAULT_CALENDAR_DATE(d.calendar, d.date_value, NULL),
|
|
117
|
+
CASE WHEN d.calendar = 'lunar' THEN d.first_on END
|
|
118
|
+
) AS occurs_on
|
|
119
|
+
FROM dates AS d
|
|
120
|
+
WHERE
|
|
121
|
+
d.recurrence = 'none';
|
|
122
|
+
|
|
123
|
+
/* _v_module_people */
|
|
124
|
+
/* _v_module_people */
|
|
125
|
+
CREATE VIEW _v_module_people AS
|
|
126
|
+
SELECT
|
|
127
|
+
id,
|
|
128
|
+
CASE WHEN is_self = 1 THEN 'me' ELSE 'her' END AS module
|
|
129
|
+
FROM persons
|
|
130
|
+
WHERE
|
|
131
|
+
deleted_at IS NULL AND (
|
|
132
|
+
is_self = 1 OR is_default = 1
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
/* _v_order_item_weights */
|
|
136
|
+
/* _v_order_item_weights */
|
|
137
|
+
CREATE VIEW _v_order_item_weights AS
|
|
138
|
+
WITH items AS (
|
|
139
|
+
SELECT
|
|
140
|
+
i.*,
|
|
141
|
+
COALESCE(item_amount_cents, unit_price_cents * COALESCE(quantity, 1)) AS gross_cents,
|
|
142
|
+
GREATEST(0, COALESCE(item_amount_cents, unit_price_cents * COALESCE(quantity, 1), 1)) AS raw_weight,
|
|
143
|
+
CASE
|
|
144
|
+
WHEN item_status IN ('退款成功', '已退款', '已全额退款', '退货退款成功', '交易关闭', '已关闭', '已取消')
|
|
145
|
+
THEN 1
|
|
146
|
+
ELSE 0
|
|
147
|
+
END AS refunded
|
|
148
|
+
FROM order_items AS i
|
|
149
|
+
)
|
|
150
|
+
SELECT
|
|
151
|
+
*,
|
|
152
|
+
CASE
|
|
153
|
+
WHEN SUM(raw_weight) OVER (PARTITION BY platform, order_no) = 0
|
|
154
|
+
THEN 1
|
|
155
|
+
ELSE raw_weight
|
|
156
|
+
END AS weight
|
|
157
|
+
FROM items;
|
|
158
|
+
|
|
159
|
+
/* _v_payment_bases */
|
|
160
|
+
/* _v_payment_bases */
|
|
161
|
+
CREATE VIEW _v_payment_bases AS
|
|
162
|
+
SELECT
|
|
163
|
+
p.*,
|
|
164
|
+
p.platform || ':' || p.txn_id AS target_key,
|
|
165
|
+
COALESCE(
|
|
166
|
+
NULLIF(
|
|
167
|
+
TRIM(REPLACE(CAST(JSON_EXTRACT_PATH_TEXT(p.raw_json, '交易订单号') AS TEXT), CHR( 9), '')),
|
|
168
|
+
''
|
|
169
|
+
),
|
|
170
|
+
p.txn_id
|
|
171
|
+
) AS txn_base,
|
|
172
|
+
CASE
|
|
173
|
+
WHEN p.direction <> 'expense'
|
|
174
|
+
AND (
|
|
175
|
+
p.status IN ('退款成功', '已退款', '已全额退款', '对方已退还')
|
|
176
|
+
OR (
|
|
177
|
+
(
|
|
178
|
+
p.platform_category = '退款' OR p.txn_type = '退款'
|
|
179
|
+
)
|
|
180
|
+
AND p.status IN ('成功', '交易成功', '支付成功')
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
THEN 1
|
|
184
|
+
ELSE 0
|
|
185
|
+
END AS is_refund
|
|
186
|
+
FROM payments AS p;
|
|
187
|
+
|
|
188
|
+
/* _v_payment_refund_matches */
|
|
189
|
+
/* _v_payment_refund_matches */
|
|
190
|
+
CREATE VIEW _v_payment_refund_matches AS
|
|
191
|
+
WITH candidates AS (
|
|
192
|
+
SELECT
|
|
193
|
+
e.platform,
|
|
194
|
+
e.txn_id,
|
|
195
|
+
r.target_key AS refund_key,
|
|
196
|
+
r.amount_cents,
|
|
197
|
+
CASE WHEN e.txn_base = r.txn_base THEN 0 ELSE 1 END AS priority
|
|
198
|
+
FROM _v_payment_bases AS e
|
|
199
|
+
JOIN _v_payment_bases AS r
|
|
200
|
+
ON r.platform = e.platform
|
|
201
|
+
AND r.is_refund = 1
|
|
202
|
+
AND e.direction = 'expense'
|
|
203
|
+
AND r.occurred_at >= e.occurred_at
|
|
204
|
+
AND (
|
|
205
|
+
e.txn_base = r.txn_base
|
|
206
|
+
OR (
|
|
207
|
+
NOT NULLIF(TRIM(e.merchant_order_no), '') IS NULL
|
|
208
|
+
AND e.merchant_order_no <> '/'
|
|
209
|
+
AND TRIM(e.merchant_order_no) = TRIM(r.merchant_order_no)
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
), ranked AS (
|
|
213
|
+
SELECT
|
|
214
|
+
*,
|
|
215
|
+
MIN(priority) OVER (PARTITION BY refund_key) AS best
|
|
216
|
+
FROM candidates
|
|
217
|
+
), unique_matches AS (
|
|
218
|
+
SELECT
|
|
219
|
+
*,
|
|
220
|
+
COUNT(*) OVER (PARTITION BY refund_key) AS matches
|
|
221
|
+
FROM ranked
|
|
222
|
+
WHERE
|
|
223
|
+
priority = best
|
|
224
|
+
)
|
|
225
|
+
SELECT
|
|
226
|
+
platform,
|
|
227
|
+
txn_id,
|
|
228
|
+
refund_key,
|
|
229
|
+
amount_cents
|
|
230
|
+
FROM unique_matches
|
|
231
|
+
WHERE
|
|
232
|
+
matches = 1;
|
|
233
|
+
|
|
234
|
+
/* _v_trusted_payment_orders */
|
|
235
|
+
/* _v_trusted_payment_orders */
|
|
236
|
+
CREATE VIEW _v_trusted_payment_orders AS
|
|
237
|
+
SELECT
|
|
238
|
+
*
|
|
239
|
+
FROM payment_order_links
|
|
240
|
+
WHERE
|
|
241
|
+
deleted_at IS NULL AND (
|
|
242
|
+
confidence = 'high' OR match_method = 'manual'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
/* v_payments */
|
|
246
|
+
/* v_payments */
|
|
247
|
+
CREATE VIEW v_payments AS
|
|
248
|
+
WITH identity_candidates AS (
|
|
249
|
+
SELECT
|
|
250
|
+
p.target_key,
|
|
251
|
+
i.person_id,
|
|
252
|
+
CASE WHEN i.match_kind = 'account_id' THEN 0 ELSE 1 END AS priority
|
|
253
|
+
FROM _v_payment_bases AS p
|
|
254
|
+
JOIN identities AS i
|
|
255
|
+
ON i.source = p.platform
|
|
256
|
+
AND i.deleted_at IS NULL
|
|
257
|
+
AND (
|
|
258
|
+
(
|
|
259
|
+
i.match_kind = 'account_id'
|
|
260
|
+
AND VAULT_IDENTITY_MATCH(i.match_mode, i."value", p.counterparty_account) = 1
|
|
261
|
+
)
|
|
262
|
+
OR (
|
|
263
|
+
i.match_kind = 'counterparty'
|
|
264
|
+
AND VAULT_IDENTITY_MATCH(i.match_mode, i."value", p.counterparty) = 1
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
AND (
|
|
268
|
+
i.valid_from IS NULL OR i.valid_from <= SUBSTRING(p.occurred_at FROM 1 FOR 10)
|
|
269
|
+
)
|
|
270
|
+
AND (
|
|
271
|
+
i.valid_to IS NULL OR i.valid_to >= SUBSTRING(p.occurred_at FROM 1 FOR 10)
|
|
272
|
+
)
|
|
273
|
+
JOIN persons AS person
|
|
274
|
+
ON person.id = i.person_id AND person.deleted_at IS NULL
|
|
275
|
+
), ranked AS (
|
|
276
|
+
SELECT
|
|
277
|
+
*,
|
|
278
|
+
MIN(priority) OVER (PARTITION BY target_key) AS best
|
|
279
|
+
FROM identity_candidates
|
|
280
|
+
), matched AS (
|
|
281
|
+
SELECT
|
|
282
|
+
target_key,
|
|
283
|
+
MIN(person_id) AS person_id,
|
|
284
|
+
COUNT(DISTINCT person_id) AS persons
|
|
285
|
+
FROM ranked
|
|
286
|
+
WHERE
|
|
287
|
+
priority = best
|
|
288
|
+
GROUP BY
|
|
289
|
+
target_key
|
|
290
|
+
), refunds AS (
|
|
291
|
+
SELECT
|
|
292
|
+
platform,
|
|
293
|
+
txn_id,
|
|
294
|
+
SUM(amount_cents) AS refund_cents
|
|
295
|
+
FROM _v_payment_refund_matches
|
|
296
|
+
GROUP BY
|
|
297
|
+
platform,
|
|
298
|
+
txn_id
|
|
299
|
+
), enriched AS (
|
|
300
|
+
SELECT
|
|
301
|
+
p.*,
|
|
302
|
+
CASE WHEN i.persons = 1 THEN i.person_id END AS counterparty_person_id,
|
|
303
|
+
CASE WHEN i.persons = 1 THEN person.display_name END AS counterparty_person_name,
|
|
304
|
+
COALESCE(CAST(i.persons > 1 AS INT), 0) AS identity_ambiguous,
|
|
305
|
+
a.category AS analysis_category,
|
|
306
|
+
COALESCE(a.hidden, 0) AS hidden,
|
|
307
|
+
COALESCE(a.sensitivity, 'private') AS sensitivity,
|
|
308
|
+
LEAST(
|
|
309
|
+
p.amount_cents,
|
|
310
|
+
GREATEST(
|
|
311
|
+
COALESCE(p.refund_cents, 0),
|
|
312
|
+
COALESCE(r.refund_cents, 0),
|
|
313
|
+
CASE
|
|
314
|
+
WHEN p.status IN ('已全额退款', '对方已退还', '已退还', '退款成功', '已退款')
|
|
315
|
+
THEN p.amount_cents
|
|
316
|
+
ELSE 0
|
|
317
|
+
END
|
|
318
|
+
)
|
|
319
|
+
) AS effective_refund_cents,
|
|
320
|
+
CASE
|
|
321
|
+
WHEN POSITION(
|
|
322
|
+
'转账' IN COALESCE(p.txn_type, '') || ' ' || COALESCE(p.platform_category, '') || ' ' || COALESCE(a.category, '')
|
|
323
|
+
) > 0
|
|
324
|
+
OR POSITION(
|
|
325
|
+
'红包' IN COALESCE(p.txn_type, '') || ' ' || COALESCE(p.platform_category, '') || ' ' || COALESCE(a.category, '')
|
|
326
|
+
) > 0
|
|
327
|
+
THEN 1
|
|
328
|
+
ELSE 0
|
|
329
|
+
END AS is_transfer
|
|
330
|
+
FROM _v_payment_bases AS p
|
|
331
|
+
LEFT JOIN refunds AS r
|
|
332
|
+
ON r.platform = p.platform AND r.txn_id = p.txn_id
|
|
333
|
+
LEFT JOIN matched AS i
|
|
334
|
+
ON i.target_key = p.target_key
|
|
335
|
+
LEFT JOIN persons AS person
|
|
336
|
+
ON person.id = i.person_id
|
|
337
|
+
LEFT JOIN _v_annotations AS a
|
|
338
|
+
ON a.target_type = 'payment' AND a.target_key = p.target_key
|
|
339
|
+
)
|
|
340
|
+
SELECT
|
|
341
|
+
*,
|
|
342
|
+
amount_cents / NULLIF(100.0, 0) AS amount_yuan,
|
|
343
|
+
CASE
|
|
344
|
+
WHEN status IN ('交易关闭', '已关闭', '已取消', '待付款', '等待买家付款')
|
|
345
|
+
THEN 0
|
|
346
|
+
ELSE amount_cents - effective_refund_cents
|
|
347
|
+
END AS net_amount_cents,
|
|
348
|
+
CASE
|
|
349
|
+
WHEN status IN ('交易关闭', '已关闭', '已取消', '待付款', '等待买家付款')
|
|
350
|
+
THEN 0
|
|
351
|
+
ELSE (
|
|
352
|
+
amount_cents - effective_refund_cents
|
|
353
|
+
) / NULLIF(100.0, 0)
|
|
354
|
+
END AS net_amount_yuan
|
|
355
|
+
FROM enriched;
|
|
356
|
+
|
|
357
|
+
/* _v_order_payment_allocations */
|
|
358
|
+
/* _v_order_payment_allocations */
|
|
359
|
+
CREATE VIEW _v_order_payment_allocations AS
|
|
360
|
+
WITH weights AS (
|
|
361
|
+
SELECT
|
|
362
|
+
l.*,
|
|
363
|
+
p.amount_cents,
|
|
364
|
+
p.effective_refund_cents,
|
|
365
|
+
p.direction,
|
|
366
|
+
p.is_refund,
|
|
367
|
+
p.hidden,
|
|
368
|
+
CASE WHEN o.total_paid_cents > 0 THEN o.total_paid_cents ELSE 1 END AS weight,
|
|
369
|
+
CASE
|
|
370
|
+
WHEN p.is_refund = 1
|
|
371
|
+
AND NOT EXISTS(
|
|
372
|
+
SELECT
|
|
373
|
+
1
|
|
374
|
+
FROM _v_payment_refund_matches AS r
|
|
375
|
+
WHERE
|
|
376
|
+
r.refund_key = p.target_key
|
|
377
|
+
)
|
|
378
|
+
THEN p.amount_cents
|
|
379
|
+
WHEN p.direction = 'expense'
|
|
380
|
+
THEN p.effective_refund_cents
|
|
381
|
+
ELSE 0
|
|
382
|
+
END AS refund_cents
|
|
383
|
+
FROM _v_trusted_payment_orders AS l
|
|
384
|
+
JOIN v_payments AS p
|
|
385
|
+
ON p.platform = l.payment_platform AND p.txn_id = l.payment_txn_id
|
|
386
|
+
JOIN orders AS o
|
|
387
|
+
ON o.platform = l.order_platform AND o.order_no = l.order_no
|
|
388
|
+
), proportions AS (
|
|
389
|
+
SELECT
|
|
390
|
+
*,
|
|
391
|
+
SUM(weight) OVER (PARTITION BY payment_platform, payment_txn_id) AS total_weight,
|
|
392
|
+
ROW_NUMBER() OVER (
|
|
393
|
+
PARTITION BY payment_platform, payment_txn_id
|
|
394
|
+
ORDER BY order_platform NULLS FIRST, order_no NULLS FIRST
|
|
395
|
+
) AS ordinal
|
|
396
|
+
FROM weights
|
|
397
|
+
), rounded AS (
|
|
398
|
+
SELECT
|
|
399
|
+
*,
|
|
400
|
+
CAST(TRUNC(amount_cents * CAST(1.0 AS DOUBLE PRECISION) * weight / NULLIF(total_weight, 0)) AS BIGINT) AS base_cents,
|
|
401
|
+
CAST(TRUNC(refund_cents * CAST(1.0 AS DOUBLE PRECISION) * weight / NULLIF(total_weight, 0)) AS BIGINT) AS base_refund_cents
|
|
402
|
+
FROM proportions
|
|
403
|
+
)
|
|
404
|
+
SELECT
|
|
405
|
+
*,
|
|
406
|
+
base_cents + CASE
|
|
407
|
+
WHEN ordinal = 1
|
|
408
|
+
THEN amount_cents - SUM(base_cents) OVER (PARTITION BY payment_platform, payment_txn_id)
|
|
409
|
+
ELSE 0
|
|
410
|
+
END AS allocated_cents,
|
|
411
|
+
base_refund_cents + CASE
|
|
412
|
+
WHEN ordinal = 1
|
|
413
|
+
THEN refund_cents - SUM(base_refund_cents) OVER (PARTITION BY payment_platform, payment_txn_id)
|
|
414
|
+
ELSE 0
|
|
415
|
+
END AS allocated_refund_cents
|
|
416
|
+
FROM rounded;
|
|
417
|
+
|
|
418
|
+
/* _v_order_net */
|
|
419
|
+
/* _v_order_net */
|
|
420
|
+
CREATE VIEW _v_order_net AS
|
|
421
|
+
WITH paid AS (
|
|
422
|
+
SELECT
|
|
423
|
+
order_platform,
|
|
424
|
+
order_no,
|
|
425
|
+
SUM(CASE WHEN direction = 'expense' THEN allocated_cents END) AS gross_cents,
|
|
426
|
+
SUM(allocated_refund_cents) AS refund_cents,
|
|
427
|
+
MAX(hidden) AS payment_hidden,
|
|
428
|
+
COUNT(
|
|
429
|
+
DISTINCT CASE WHEN direction = 'expense' THEN payment_platform || ':' || payment_txn_id END
|
|
430
|
+
) AS payment_count,
|
|
431
|
+
MIN(
|
|
432
|
+
CASE WHEN direction = 'expense' THEN payment_platform || ':' || payment_txn_id END
|
|
433
|
+
) AS payment_key
|
|
434
|
+
FROM _v_order_payment_allocations
|
|
435
|
+
GROUP BY
|
|
436
|
+
order_platform,
|
|
437
|
+
order_no
|
|
438
|
+
), items AS (
|
|
439
|
+
SELECT
|
|
440
|
+
platform,
|
|
441
|
+
order_no,
|
|
442
|
+
CASE WHEN COUNT(gross_cents) = COUNT(*) THEN SUM(gross_cents) END AS gross_cents,
|
|
443
|
+
SUM(weight) AS total_weight,
|
|
444
|
+
SUM(CASE WHEN refunded = 1 THEN weight ELSE 0 END) AS refunded_weight
|
|
445
|
+
FROM _v_order_item_weights
|
|
446
|
+
GROUP BY
|
|
447
|
+
platform,
|
|
448
|
+
order_no
|
|
449
|
+
), amounts AS (
|
|
450
|
+
SELECT
|
|
451
|
+
o.*,
|
|
452
|
+
COALESCE(o.total_paid_cents, p.gross_cents, o.goods_total_cents, i.gross_cents) AS gross_cents,
|
|
453
|
+
COALESCE(p.refund_cents, 0) AS payment_refund_cents,
|
|
454
|
+
COALESCE(p.payment_hidden, 0) AS payment_hidden,
|
|
455
|
+
COALESCE(p.payment_count, 0) AS payment_count,
|
|
456
|
+
p.payment_key,
|
|
457
|
+
COALESCE(
|
|
458
|
+
i.refunded_weight * CAST(1.0 AS DOUBLE PRECISION) / NULLIF(NULLIF(i.total_weight, 0), 0),
|
|
459
|
+
0
|
|
460
|
+
) AS item_refund_ratio,
|
|
461
|
+
CASE
|
|
462
|
+
WHEN NOT o.total_paid_cents IS NULL
|
|
463
|
+
THEN 'order_paid'
|
|
464
|
+
WHEN NOT p.gross_cents IS NULL
|
|
465
|
+
THEN 'linked_payment'
|
|
466
|
+
WHEN NOT o.goods_total_cents IS NULL
|
|
467
|
+
THEN 'order_goods'
|
|
468
|
+
WHEN NOT i.gross_cents IS NULL
|
|
469
|
+
THEN 'item_total'
|
|
470
|
+
ELSE 'unknown'
|
|
471
|
+
END AS amount_basis
|
|
472
|
+
FROM orders AS o
|
|
473
|
+
LEFT JOIN paid AS p
|
|
474
|
+
ON p.order_platform = o.platform AND p.order_no = o.order_no
|
|
475
|
+
LEFT JOIN items AS i
|
|
476
|
+
ON i.platform = o.platform AND i.order_no = o.order_no
|
|
477
|
+
)
|
|
478
|
+
SELECT
|
|
479
|
+
*,
|
|
480
|
+
CASE
|
|
481
|
+
WHEN status IN ('交易关闭', '已关闭', '已取消', '待付款', '等待买家付款', '退款成功', '已退款', '已全额退款', '退货退款成功')
|
|
482
|
+
THEN 0
|
|
483
|
+
WHEN gross_cents IS NULL
|
|
484
|
+
THEN NULL
|
|
485
|
+
ELSE GREATEST(
|
|
486
|
+
0,
|
|
487
|
+
gross_cents - GREATEST(
|
|
488
|
+
payment_refund_cents,
|
|
489
|
+
CAST(TRUNC(ROUND(CAST(gross_cents * item_refund_ratio AS DECIMAL))) AS BIGINT)
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
END AS net_cents,
|
|
493
|
+
CASE
|
|
494
|
+
WHEN payment_refund_cents > 0
|
|
495
|
+
AND payment_refund_cents >= ROUND(CAST(gross_cents * item_refund_ratio AS DECIMAL))
|
|
496
|
+
THEN 'payment'
|
|
497
|
+
WHEN item_refund_ratio > 0
|
|
498
|
+
THEN 'item_status_allocation'
|
|
499
|
+
ELSE 'none'
|
|
500
|
+
END AS refund_basis,
|
|
501
|
+
CASE
|
|
502
|
+
WHEN amount_basis IN ('order_goods', 'item_total', 'unknown')
|
|
503
|
+
OR ROUND(CAST(gross_cents * item_refund_ratio AS DECIMAL)) > payment_refund_cents
|
|
504
|
+
THEN 1
|
|
505
|
+
ELSE 0
|
|
506
|
+
END AS amount_is_estimated
|
|
507
|
+
FROM amounts;
|
|
508
|
+
|
|
509
|
+
/* v_spending */
|
|
510
|
+
/* v_spending */
|
|
511
|
+
CREATE VIEW v_spending AS
|
|
512
|
+
WITH order_lines AS (
|
|
513
|
+
SELECT
|
|
514
|
+
o.platform,
|
|
515
|
+
o.order_no,
|
|
516
|
+
i.item_key,
|
|
517
|
+
COALESCE(o.paid_at, o.ordered_at) AS occurred_at,
|
|
518
|
+
COALESCE(i.product_name, o.shop_name, '订单') AS title,
|
|
519
|
+
o.shop_name,
|
|
520
|
+
o.gross_cents AS order_gross_cents,
|
|
521
|
+
o.net_cents,
|
|
522
|
+
o.amount_basis,
|
|
523
|
+
o.refund_basis,
|
|
524
|
+
o.amount_is_estimated,
|
|
525
|
+
COALESCE(i.weight, 1) AS weight,
|
|
526
|
+
o.payment_count,
|
|
527
|
+
o.payment_key,
|
|
528
|
+
GREATEST(COALESCE(oa.hidden, 0), COALESCE(ia.hidden, 0), o.payment_hidden) AS hidden,
|
|
529
|
+
COALESCE(ia.category, oa.category, pa.category) AS category,
|
|
530
|
+
CASE
|
|
531
|
+
WHEN ia.sensitivity = 'intimate'
|
|
532
|
+
OR oa.sensitivity = 'intimate'
|
|
533
|
+
OR pa.sensitivity = 'intimate'
|
|
534
|
+
THEN 'intimate'
|
|
535
|
+
ELSE COALESCE(ia.sensitivity, oa.sensitivity, pa.sensitivity, 'private')
|
|
536
|
+
END AS sensitivity,
|
|
537
|
+
CAST(COALESCE(
|
|
538
|
+
ib.beneficiaries_json,
|
|
539
|
+
ob.beneficiaries_json,
|
|
540
|
+
CASE WHEN o.payment_count = 1 THEN pb.beneficiaries_json END,
|
|
541
|
+
'[]'
|
|
542
|
+
) AS TEXT) AS beneficiaries_json
|
|
543
|
+
FROM _v_order_net AS o
|
|
544
|
+
LEFT JOIN _v_order_item_weights AS i
|
|
545
|
+
ON i.platform = o.platform AND i.order_no = o.order_no
|
|
546
|
+
LEFT JOIN _v_annotations AS oa
|
|
547
|
+
ON oa.target_type = 'order' AND oa.target_key = o.platform || ':' || o.order_no
|
|
548
|
+
LEFT JOIN _v_annotations AS ia
|
|
549
|
+
ON ia.target_type = 'order_item'
|
|
550
|
+
AND ia.target_key = o.platform || ':' || o.order_no || ':' || i.item_key
|
|
551
|
+
LEFT JOIN _v_annotations AS pa
|
|
552
|
+
ON pa.target_type = 'payment' AND pa.target_key = o.payment_key
|
|
553
|
+
LEFT JOIN _v_beneficiary_groups AS ob
|
|
554
|
+
ON ob.target_type = 'order' AND ob.target_key = o.platform || ':' || o.order_no
|
|
555
|
+
LEFT JOIN _v_beneficiary_groups AS ib
|
|
556
|
+
ON ib.target_type = 'order_item'
|
|
557
|
+
AND ib.target_key = o.platform || ':' || o.order_no || ':' || i.item_key
|
|
558
|
+
LEFT JOIN _v_beneficiary_groups AS pb
|
|
559
|
+
ON pb.target_type = 'payment' AND pb.target_key = o.payment_key
|
|
560
|
+
WHERE
|
|
561
|
+
COALESCE(i.refunded, 0) = 0
|
|
562
|
+
), weights AS (
|
|
563
|
+
SELECT
|
|
564
|
+
*,
|
|
565
|
+
SUM(weight) OVER (PARTITION BY platform, order_no) AS total_weight,
|
|
566
|
+
ROW_NUMBER() OVER (
|
|
567
|
+
PARTITION BY platform, order_no
|
|
568
|
+
ORDER BY (
|
|
569
|
+
weight > 0
|
|
570
|
+
) DESC NULLS LAST, item_key NULLS FIRST
|
|
571
|
+
) AS ordinal
|
|
572
|
+
FROM order_lines
|
|
573
|
+
), rounded AS (
|
|
574
|
+
SELECT
|
|
575
|
+
*,
|
|
576
|
+
CAST(TRUNC(net_cents * CAST(1.0 AS DOUBLE PRECISION) * weight / NULLIF(total_weight, 0)) AS BIGINT) AS base_cents
|
|
577
|
+
FROM weights
|
|
578
|
+
), allocated AS (
|
|
579
|
+
SELECT
|
|
580
|
+
*,
|
|
581
|
+
base_cents + CASE
|
|
582
|
+
WHEN ordinal = 1
|
|
583
|
+
THEN net_cents - SUM(base_cents) OVER (PARTITION BY platform, order_no)
|
|
584
|
+
ELSE 0
|
|
585
|
+
END AS amount_cents
|
|
586
|
+
FROM rounded
|
|
587
|
+
), rows AS (
|
|
588
|
+
SELECT
|
|
589
|
+
CASE WHEN item_key IS NULL THEN 'order' ELSE 'order_item' END AS source_type,
|
|
590
|
+
platform || ':' || order_no || CASE WHEN item_key IS NULL THEN '' ELSE ':' || item_key END AS source_key,
|
|
591
|
+
platform,
|
|
592
|
+
order_no,
|
|
593
|
+
item_key,
|
|
594
|
+
occurred_at,
|
|
595
|
+
title,
|
|
596
|
+
shop_name,
|
|
597
|
+
category,
|
|
598
|
+
sensitivity,
|
|
599
|
+
amount_cents,
|
|
600
|
+
amount_basis,
|
|
601
|
+
refund_basis,
|
|
602
|
+
amount_is_estimated,
|
|
603
|
+
beneficiaries_json,
|
|
604
|
+
hidden,
|
|
605
|
+
CASE
|
|
606
|
+
WHEN title LIKE '%火车票%'
|
|
607
|
+
OR title LIKE '%高铁票%'
|
|
608
|
+
OR title LIKE '%12306%'
|
|
609
|
+
OR shop_name LIKE '%火车票%'
|
|
610
|
+
OR shop_name LIKE '%铁路%'
|
|
611
|
+
OR shop_name LIKE '%12306%'
|
|
612
|
+
OR category IN ('火车票', '车票', 'train_ticket', 'train')
|
|
613
|
+
THEN 1
|
|
614
|
+
ELSE 0
|
|
615
|
+
END AS is_train,
|
|
616
|
+
CAST(EXISTS(
|
|
617
|
+
SELECT
|
|
618
|
+
1
|
|
619
|
+
FROM payment_order_links AS l
|
|
620
|
+
WHERE
|
|
621
|
+
l.order_platform = allocated.platform
|
|
622
|
+
AND l.order_no = allocated.order_no
|
|
623
|
+
AND l.deleted_at IS NULL
|
|
624
|
+
AND l.confidence <> 'high'
|
|
625
|
+
AND l.match_method <> 'manual'
|
|
626
|
+
) AS INT) AS needs_match_review
|
|
627
|
+
FROM allocated
|
|
628
|
+
UNION ALL
|
|
629
|
+
SELECT
|
|
630
|
+
'payment',
|
|
631
|
+
p.target_key,
|
|
632
|
+
p.platform,
|
|
633
|
+
NULL,
|
|
634
|
+
NULL,
|
|
635
|
+
p.occurred_at,
|
|
636
|
+
COALESCE(p.item_desc, p.counterparty, '支付'),
|
|
637
|
+
p.counterparty,
|
|
638
|
+
COALESCE(p.analysis_category, p.platform_category),
|
|
639
|
+
p.sensitivity,
|
|
640
|
+
p.net_amount_cents,
|
|
641
|
+
'payment',
|
|
642
|
+
CASE WHEN p.effective_refund_cents > 0 THEN 'payment' ELSE 'none' END,
|
|
643
|
+
0,
|
|
644
|
+
COALESCE(b.beneficiaries_json, '[]'),
|
|
645
|
+
p.hidden,
|
|
646
|
+
CASE
|
|
647
|
+
WHEN p.item_desc LIKE '%火车票%'
|
|
648
|
+
OR p.item_desc LIKE '%高铁票%'
|
|
649
|
+
OR p.item_desc LIKE '%12306%'
|
|
650
|
+
OR p.counterparty LIKE '%铁路%'
|
|
651
|
+
OR p.counterparty LIKE '%12306%'
|
|
652
|
+
OR p.analysis_category IN ('火车票', '车票', 'train_ticket', 'train')
|
|
653
|
+
THEN 1
|
|
654
|
+
ELSE 0
|
|
655
|
+
END,
|
|
656
|
+
CAST(EXISTS(
|
|
657
|
+
SELECT
|
|
658
|
+
1
|
|
659
|
+
FROM payment_order_links AS l
|
|
660
|
+
WHERE
|
|
661
|
+
l.payment_platform = p.platform
|
|
662
|
+
AND l.payment_txn_id = p.txn_id
|
|
663
|
+
AND l.deleted_at IS NULL
|
|
664
|
+
AND l.confidence <> 'high'
|
|
665
|
+
AND l.match_method <> 'manual'
|
|
666
|
+
) AS INT)
|
|
667
|
+
FROM v_payments AS p
|
|
668
|
+
LEFT JOIN _v_beneficiary_groups AS b
|
|
669
|
+
ON b.target_type = 'payment' AND b.target_key = p.target_key
|
|
670
|
+
WHERE
|
|
671
|
+
p.direction = 'expense'
|
|
672
|
+
AND p.is_refund = 0
|
|
673
|
+
AND p.is_transfer = 0
|
|
674
|
+
AND NOT EXISTS(
|
|
675
|
+
SELECT
|
|
676
|
+
1
|
|
677
|
+
FROM _v_trusted_payment_orders AS l
|
|
678
|
+
WHERE
|
|
679
|
+
l.payment_platform = p.platform AND l.payment_txn_id = p.txn_id
|
|
680
|
+
)
|
|
681
|
+
)
|
|
682
|
+
SELECT
|
|
683
|
+
source_type,
|
|
684
|
+
source_key,
|
|
685
|
+
platform,
|
|
686
|
+
order_no,
|
|
687
|
+
item_key,
|
|
688
|
+
occurred_at,
|
|
689
|
+
title,
|
|
690
|
+
shop_name,
|
|
691
|
+
category,
|
|
692
|
+
sensitivity,
|
|
693
|
+
amount_cents,
|
|
694
|
+
amount_cents / NULLIF(100.0, 0) AS amount_yuan,
|
|
695
|
+
amount_basis,
|
|
696
|
+
refund_basis,
|
|
697
|
+
amount_is_estimated,
|
|
698
|
+
beneficiaries_json,
|
|
699
|
+
needs_match_review
|
|
700
|
+
FROM rows
|
|
701
|
+
WHERE
|
|
702
|
+
hidden = 0 AND is_train = 0 AND (
|
|
703
|
+
amount_cents IS NULL OR amount_cents > 0
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
/* _v_purchase_costs */
|
|
707
|
+
/* _v_purchase_costs */
|
|
708
|
+
CREATE VIEW _v_purchase_costs AS
|
|
709
|
+
SELECT
|
|
710
|
+
source_type,
|
|
711
|
+
source_key,
|
|
712
|
+
CASE WHEN needs_match_review = 0 THEN amount_cents END AS amount_cents,
|
|
713
|
+
CAST(amount_cents IS NULL AS INT) AS unknown_amount_count,
|
|
714
|
+
needs_match_review AS pending_match_count,
|
|
715
|
+
CAST((
|
|
716
|
+
amount_is_estimated = 1 AND NOT amount_cents IS NULL
|
|
717
|
+
) AS INT) AS estimated_count,
|
|
718
|
+
sensitivity
|
|
719
|
+
FROM v_spending
|
|
720
|
+
WHERE
|
|
721
|
+
source_type <> 'order'
|
|
722
|
+
UNION ALL
|
|
723
|
+
SELECT
|
|
724
|
+
'order',
|
|
725
|
+
platform || ':' || order_no,
|
|
726
|
+
SUM(CASE WHEN needs_match_review = 0 THEN amount_cents END),
|
|
727
|
+
SUM(CAST(amount_cents IS NULL AS INT)),
|
|
728
|
+
SUM(needs_match_review),
|
|
729
|
+
SUM(CAST(amount_is_estimated = 1 AND NOT amount_cents IS NULL AS INT)),
|
|
730
|
+
CASE
|
|
731
|
+
WHEN SUM(CAST(sensitivity = 'intimate' AS INT)) > 0
|
|
732
|
+
THEN 'intimate'
|
|
733
|
+
WHEN SUM(CAST(sensitivity = 'private' AS INT)) > 0
|
|
734
|
+
THEN 'private'
|
|
735
|
+
ELSE 'normal'
|
|
736
|
+
END
|
|
737
|
+
FROM v_spending
|
|
738
|
+
WHERE
|
|
739
|
+
NOT order_no IS NULL
|
|
740
|
+
GROUP BY
|
|
741
|
+
platform,
|
|
742
|
+
order_no;
|
|
743
|
+
|
|
744
|
+
/* _v_spending_share_rows */
|
|
745
|
+
/* _v_spending_share_rows */
|
|
746
|
+
CREATE VIEW _v_spending_share_rows AS
|
|
747
|
+
WITH shares AS (
|
|
748
|
+
SELECT
|
|
749
|
+
s.source_type,
|
|
750
|
+
s.source_key,
|
|
751
|
+
s.amount_cents,
|
|
752
|
+
JSON_EXTRACT_PATH_TEXT(j."value", 'person_id') AS person_id,
|
|
753
|
+
CAST(JSON_EXTRACT_PATH_TEXT(j."value", 'share_ratio') AS DOUBLE PRECISION) AS ratio
|
|
754
|
+
FROM v_spending AS s
|
|
755
|
+
CROSS JOIN JSON_EACH(s.beneficiaries_json) AS j
|
|
756
|
+
), checked AS (
|
|
757
|
+
SELECT
|
|
758
|
+
*,
|
|
759
|
+
SUM(ratio) OVER (PARTITION BY source_type, source_key) AS total_ratio,
|
|
760
|
+
MIN(CASE WHEN ratio IS NOT NULL AND ratio BETWEEN 0 AND 1 THEN 1 ELSE 0 END) OVER (PARTITION BY source_type, source_key) AS ratios_valid,
|
|
761
|
+
ROW_NUMBER() OVER (
|
|
762
|
+
PARTITION BY source_type, source_key
|
|
763
|
+
ORDER BY (
|
|
764
|
+
ratio > 0
|
|
765
|
+
) DESC NULLS LAST, person_id NULLS FIRST
|
|
766
|
+
) AS ordinal
|
|
767
|
+
FROM shares
|
|
768
|
+
), rounded AS (
|
|
769
|
+
SELECT
|
|
770
|
+
*,
|
|
771
|
+
CAST(TRUNC(amount_cents * ratio) AS BIGINT) AS base_cents
|
|
772
|
+
FROM checked
|
|
773
|
+
)
|
|
774
|
+
SELECT
|
|
775
|
+
*,
|
|
776
|
+
CASE
|
|
777
|
+
WHEN ratios_valid = 1 AND total_ratio <= 1.000000001
|
|
778
|
+
THEN base_cents + CASE
|
|
779
|
+
WHEN ordinal = 1
|
|
780
|
+
THEN CAST(TRUNC(ROUND(CAST(amount_cents * LEAST(total_ratio, 1) AS DECIMAL))) AS BIGINT) - SUM(base_cents) OVER (PARTITION BY source_type, source_key)
|
|
781
|
+
ELSE 0
|
|
782
|
+
END
|
|
783
|
+
END AS allocated_cents,
|
|
784
|
+
CASE WHEN ratios_valid = 1 AND total_ratio <= 1.000000001 THEN 0 ELSE 1 END AS invalid_shares
|
|
785
|
+
FROM rounded;
|
|
786
|
+
|
|
787
|
+
/* _v_spending_quality */
|
|
788
|
+
/* _v_spending_quality */
|
|
789
|
+
CREATE VIEW _v_spending_quality AS
|
|
790
|
+
WITH allocations AS (
|
|
791
|
+
SELECT
|
|
792
|
+
source_type,
|
|
793
|
+
source_key,
|
|
794
|
+
SUM(allocated_cents) AS allocated_cents,
|
|
795
|
+
MAX(invalid_shares) AS invalid_shares
|
|
796
|
+
FROM _v_spending_share_rows
|
|
797
|
+
GROUP BY
|
|
798
|
+
source_type,
|
|
799
|
+
source_key
|
|
800
|
+
)
|
|
801
|
+
SELECT
|
|
802
|
+
s.*,
|
|
803
|
+
COALESCE(a.invalid_shares, 0) AS invalid_shares,
|
|
804
|
+
CASE
|
|
805
|
+
WHEN s.amount_cents IS NULL
|
|
806
|
+
THEN NULL
|
|
807
|
+
ELSE s.amount_cents - COALESCE(a.allocated_cents, 0)
|
|
808
|
+
END AS unassigned_cents
|
|
809
|
+
FROM v_spending AS s
|
|
810
|
+
LEFT JOIN allocations AS a
|
|
811
|
+
ON a.source_type = s.source_type AND a.source_key = s.source_key;
|
|
812
|
+
|
|
813
|
+
/* v_messages */
|
|
814
|
+
/* v_messages */
|
|
815
|
+
CREATE VIEW v_messages AS
|
|
816
|
+
WITH matched AS (
|
|
817
|
+
SELECT
|
|
818
|
+
m.msg_id,
|
|
819
|
+
MIN(i.person_id) AS person_id,
|
|
820
|
+
COUNT(DISTINCT i.person_id) AS persons
|
|
821
|
+
FROM wechat_messages AS m
|
|
822
|
+
JOIN wechat_sessions AS s
|
|
823
|
+
ON s.session_key = m.session_key
|
|
824
|
+
JOIN identities AS i
|
|
825
|
+
ON i.source = s.platform
|
|
826
|
+
AND i.match_kind = 'account_id'
|
|
827
|
+
AND VAULT_IDENTITY_MATCH(i.match_mode, i."value", m.sender_platform_id) = 1
|
|
828
|
+
AND i.deleted_at IS NULL
|
|
829
|
+
AND (
|
|
830
|
+
i.valid_from IS NULL OR i.valid_from <= SUBSTRING(m.sent_at FROM 1 FOR 10)
|
|
831
|
+
)
|
|
832
|
+
AND (
|
|
833
|
+
i.valid_to IS NULL OR i.valid_to >= SUBSTRING(m.sent_at FROM 1 FOR 10)
|
|
834
|
+
)
|
|
835
|
+
JOIN persons AS p
|
|
836
|
+
ON p.id = i.person_id AND p.deleted_at IS NULL
|
|
837
|
+
GROUP BY
|
|
838
|
+
m.msg_id
|
|
839
|
+
)
|
|
840
|
+
SELECT
|
|
841
|
+
m.*,
|
|
842
|
+
s.name AS session_name,
|
|
843
|
+
s.session_type,
|
|
844
|
+
CASE WHEN i.persons = 1 THEN i.person_id END AS person_id,
|
|
845
|
+
CASE WHEN i.persons = 1 THEN p.display_name END AS person_name,
|
|
846
|
+
COALESCE(CAST(i.persons > 1 AS INT), 0) AS identity_ambiguous,
|
|
847
|
+
COALESCE(a.hidden, 0) AS hidden,
|
|
848
|
+
COALESCE(a.sensitivity, 'normal') AS sensitivity
|
|
849
|
+
FROM wechat_messages AS m
|
|
850
|
+
JOIN wechat_sessions AS s
|
|
851
|
+
ON s.session_key = m.session_key
|
|
852
|
+
LEFT JOIN matched AS i
|
|
853
|
+
ON i.msg_id = m.msg_id
|
|
854
|
+
LEFT JOIN persons AS p
|
|
855
|
+
ON p.id = i.person_id
|
|
856
|
+
LEFT JOIN _v_annotations AS a
|
|
857
|
+
ON a.target_type = 'message' AND a.target_key = m.msg_id;
|
|
858
|
+
|
|
859
|
+
/* _v_us_messages */
|
|
860
|
+
/* _v_us_messages */
|
|
861
|
+
CREATE VIEW _v_us_messages AS
|
|
862
|
+
WITH messages AS MATERIALIZED (
|
|
863
|
+
SELECT
|
|
864
|
+
msg_id,
|
|
865
|
+
session_key,
|
|
866
|
+
sent_at,
|
|
867
|
+
sent_ts,
|
|
868
|
+
person_id,
|
|
869
|
+
hidden,
|
|
870
|
+
msg_type
|
|
871
|
+
FROM v_messages
|
|
872
|
+
WHERE
|
|
873
|
+
session_type = 'private' AND NOT msg_type IN (80, 81)
|
|
874
|
+
), session_days AS (
|
|
875
|
+
SELECT DISTINCT
|
|
876
|
+
m.session_key,
|
|
877
|
+
SUBSTRING(m.sent_at FROM 1 FOR 10) AS sent_on,
|
|
878
|
+
s.platform,
|
|
879
|
+
s.owner_platform_id,
|
|
880
|
+
s.peer_platform_id
|
|
881
|
+
FROM messages AS m
|
|
882
|
+
JOIN wechat_sessions AS s
|
|
883
|
+
ON s.session_key = m.session_key
|
|
884
|
+
), endpoints AS (
|
|
885
|
+
SELECT
|
|
886
|
+
session_key,
|
|
887
|
+
sent_on,
|
|
888
|
+
platform,
|
|
889
|
+
'owner' AS endpoint,
|
|
890
|
+
owner_platform_id AS "value"
|
|
891
|
+
FROM session_days
|
|
892
|
+
UNION ALL
|
|
893
|
+
SELECT
|
|
894
|
+
session_key,
|
|
895
|
+
sent_on,
|
|
896
|
+
platform,
|
|
897
|
+
'peer',
|
|
898
|
+
peer_platform_id
|
|
899
|
+
FROM session_days
|
|
900
|
+
), matched AS (
|
|
901
|
+
SELECT
|
|
902
|
+
e.session_key,
|
|
903
|
+
e.sent_on,
|
|
904
|
+
e.endpoint,
|
|
905
|
+
MIN(i.person_id) AS person_id,
|
|
906
|
+
COUNT(DISTINCT i.person_id) AS matches
|
|
907
|
+
FROM endpoints AS e
|
|
908
|
+
JOIN identities AS i
|
|
909
|
+
ON i.source = e.platform
|
|
910
|
+
AND i.match_kind = 'account_id'
|
|
911
|
+
AND i.deleted_at IS NULL
|
|
912
|
+
AND VAULT_IDENTITY_MATCH(i.match_mode, i."value", e."value") = 1
|
|
913
|
+
AND (
|
|
914
|
+
i.valid_from IS NULL OR i.valid_from <= e.sent_on
|
|
915
|
+
)
|
|
916
|
+
AND (
|
|
917
|
+
i.valid_to IS NULL OR i.valid_to >= e.sent_on
|
|
918
|
+
)
|
|
919
|
+
JOIN persons AS p
|
|
920
|
+
ON p.id = i.person_id AND p.deleted_at IS NULL
|
|
921
|
+
GROUP BY
|
|
922
|
+
e.session_key,
|
|
923
|
+
e.sent_on,
|
|
924
|
+
e.endpoint
|
|
925
|
+
), pairs AS (
|
|
926
|
+
SELECT
|
|
927
|
+
o.session_key,
|
|
928
|
+
o.sent_on
|
|
929
|
+
FROM matched AS o
|
|
930
|
+
JOIN matched AS peer
|
|
931
|
+
ON peer.session_key = o.session_key
|
|
932
|
+
AND peer.sent_on = o.sent_on
|
|
933
|
+
AND peer.endpoint = 'peer'
|
|
934
|
+
AND peer.matches = 1
|
|
935
|
+
JOIN _v_module_people AS op
|
|
936
|
+
ON op.id = o.person_id
|
|
937
|
+
JOIN _v_module_people AS pp
|
|
938
|
+
ON pp.id = peer.person_id
|
|
939
|
+
WHERE
|
|
940
|
+
o.endpoint = 'owner' AND o.matches = 1 AND op.module <> pp.module
|
|
941
|
+
)
|
|
942
|
+
SELECT
|
|
943
|
+
m.*,
|
|
944
|
+
CASE WHEN m.hidden = 0 THEN p.module END AS sender_module
|
|
945
|
+
FROM messages AS m
|
|
946
|
+
JOIN pairs AS pair
|
|
947
|
+
ON pair.session_key = m.session_key
|
|
948
|
+
AND pair.sent_on = SUBSTRING(m.sent_at FROM 1 FOR 10)
|
|
949
|
+
LEFT JOIN _v_module_people AS p
|
|
950
|
+
ON p.id = m.person_id;
|
|
951
|
+
|
|
952
|
+
/* _v_us_chat_turns */
|
|
953
|
+
/* _v_us_chat_turns */
|
|
954
|
+
CREATE VIEW _v_us_chat_turns AS
|
|
955
|
+
WITH messages AS MATERIALIZED (
|
|
956
|
+
SELECT
|
|
957
|
+
*
|
|
958
|
+
FROM _v_us_messages
|
|
959
|
+
), ties AS (
|
|
960
|
+
SELECT
|
|
961
|
+
session_key,
|
|
962
|
+
sent_ts,
|
|
963
|
+
CAST((
|
|
964
|
+
COUNT(DISTINCT sender_module) > 1 OR COUNT(*) <> COUNT(sender_module)
|
|
965
|
+
) AS INT) AS ambiguous
|
|
966
|
+
FROM messages
|
|
967
|
+
GROUP BY
|
|
968
|
+
session_key,
|
|
969
|
+
sent_ts
|
|
970
|
+
), ordered AS (
|
|
971
|
+
SELECT
|
|
972
|
+
m.*,
|
|
973
|
+
t.ambiguous,
|
|
974
|
+
LAG(sender_module) OVER history AS previous_sender,
|
|
975
|
+
LAG(t.ambiguous) OVER history AS previous_ambiguous
|
|
976
|
+
FROM messages AS m
|
|
977
|
+
JOIN ties AS t
|
|
978
|
+
ON t.session_key = m.session_key AND t.sent_ts = m.sent_ts
|
|
979
|
+
WINDOW history AS (PARTITION BY m.session_key ORDER BY m.sent_ts NULLS FIRST, m.msg_id NULLS FIRST)
|
|
980
|
+
), grouped AS (
|
|
981
|
+
SELECT
|
|
982
|
+
*,
|
|
983
|
+
SUM(
|
|
984
|
+
CASE
|
|
985
|
+
WHEN NOT sender_module IS NULL
|
|
986
|
+
AND sender_module = previous_sender
|
|
987
|
+
AND ambiguous = 0
|
|
988
|
+
AND previous_ambiguous = 0
|
|
989
|
+
THEN 0
|
|
990
|
+
ELSE 1
|
|
991
|
+
END
|
|
992
|
+
) OVER (
|
|
993
|
+
PARTITION BY session_key
|
|
994
|
+
ORDER BY sent_ts NULLS FIRST, msg_id NULLS FIRST
|
|
995
|
+
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
996
|
+
) AS turn_no
|
|
997
|
+
FROM ordered
|
|
998
|
+
)
|
|
999
|
+
SELECT
|
|
1000
|
+
session_key,
|
|
1001
|
+
turn_no,
|
|
1002
|
+
sender_module,
|
|
1003
|
+
MIN(sent_ts) AS start_ts,
|
|
1004
|
+
MAX(sent_ts) AS end_ts,
|
|
1005
|
+
MIN(sent_at) AS started_at,
|
|
1006
|
+
MAX(ambiguous) AS timing_ambiguous
|
|
1007
|
+
FROM grouped
|
|
1008
|
+
GROUP BY
|
|
1009
|
+
session_key,
|
|
1010
|
+
turn_no,
|
|
1011
|
+
sender_module;
|
|
1012
|
+
|
|
1013
|
+
/* _v_us_events */
|
|
1014
|
+
/* _v_us_events */
|
|
1015
|
+
CREATE VIEW _v_us_events AS
|
|
1016
|
+
SELECT
|
|
1017
|
+
e.*,
|
|
1018
|
+
COALESCE(a.sensitivity, e.sensitivity) AS effective_sensitivity
|
|
1019
|
+
FROM events AS e
|
|
1020
|
+
LEFT JOIN _v_annotations AS a
|
|
1021
|
+
ON a.target_type = 'events' AND a.target_key = e.id
|
|
1022
|
+
WHERE
|
|
1023
|
+
e.deleted_at IS NULL
|
|
1024
|
+
AND e.status = 'confirmed'
|
|
1025
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
1026
|
+
AND EXISTS(
|
|
1027
|
+
SELECT
|
|
1028
|
+
1
|
|
1029
|
+
FROM _v_module_people
|
|
1030
|
+
WHERE
|
|
1031
|
+
module = 'me'
|
|
1032
|
+
)
|
|
1033
|
+
AND EXISTS(
|
|
1034
|
+
SELECT
|
|
1035
|
+
1
|
|
1036
|
+
FROM _v_module_people
|
|
1037
|
+
WHERE
|
|
1038
|
+
module = 'her'
|
|
1039
|
+
)
|
|
1040
|
+
AND (
|
|
1041
|
+
NOT EXISTS(
|
|
1042
|
+
SELECT
|
|
1043
|
+
1
|
|
1044
|
+
FROM event_participants AS p
|
|
1045
|
+
WHERE
|
|
1046
|
+
p.event_id = e.id
|
|
1047
|
+
)
|
|
1048
|
+
OR (
|
|
1049
|
+
EXISTS(
|
|
1050
|
+
SELECT
|
|
1051
|
+
1
|
|
1052
|
+
FROM event_participants AS p
|
|
1053
|
+
JOIN _v_module_people AS me
|
|
1054
|
+
ON me.id = p.person_id AND me.module = 'me'
|
|
1055
|
+
WHERE
|
|
1056
|
+
p.event_id = e.id AND p.deleted_at IS NULL
|
|
1057
|
+
)
|
|
1058
|
+
AND EXISTS(
|
|
1059
|
+
SELECT
|
|
1060
|
+
1
|
|
1061
|
+
FROM event_participants AS p
|
|
1062
|
+
JOIN _v_module_people AS her
|
|
1063
|
+
ON her.id = p.person_id AND her.module = 'her'
|
|
1064
|
+
WHERE
|
|
1065
|
+
p.event_id = e.id AND p.deleted_at IS NULL
|
|
1066
|
+
)
|
|
1067
|
+
)
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
/* v_meet_days */
|
|
1071
|
+
/* v_meet_days */
|
|
1072
|
+
CREATE VIEW v_meet_days AS
|
|
1073
|
+
WITH RECURSIVE days(event_id, meet_on, end_on) AS (
|
|
1074
|
+
SELECT
|
|
1075
|
+
e.id,
|
|
1076
|
+
DATE(e.start_at),
|
|
1077
|
+
DATE(COALESCE(e.end_at, e.start_at))
|
|
1078
|
+
FROM events AS e
|
|
1079
|
+
LEFT JOIN _v_annotations AS a
|
|
1080
|
+
ON a.target_type = 'events' AND a.target_key = e.id
|
|
1081
|
+
WHERE
|
|
1082
|
+
e.kind = 'meetup'
|
|
1083
|
+
AND e.deleted_at IS NULL
|
|
1084
|
+
AND e.status = 'confirmed'
|
|
1085
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
1086
|
+
UNION ALL
|
|
1087
|
+
SELECT
|
|
1088
|
+
event_id,
|
|
1089
|
+
DATE(meet_on, '+1 day'),
|
|
1090
|
+
end_on
|
|
1091
|
+
FROM days
|
|
1092
|
+
WHERE
|
|
1093
|
+
meet_on < end_on
|
|
1094
|
+
)
|
|
1095
|
+
SELECT
|
|
1096
|
+
meet_on,
|
|
1097
|
+
COUNT(*) AS event_count,
|
|
1098
|
+
CAST(JSON_AGG(event_id) AS TEXT) AS event_ids_json
|
|
1099
|
+
FROM days
|
|
1100
|
+
GROUP BY
|
|
1101
|
+
meet_on;
|
|
1102
|
+
|
|
1103
|
+
/* v_us_meetups */
|
|
1104
|
+
/* v_us_meetups */
|
|
1105
|
+
CREATE VIEW v_us_meetups AS
|
|
1106
|
+
SELECT
|
|
1107
|
+
*,
|
|
1108
|
+
CAST(TRUNC(JULIANDAY(DATE(COALESCE(end_at, start_at))) - JULIANDAY(DATE(start_at))) AS BIGINT) + 1 AS calendar_days,
|
|
1109
|
+
CASE WHEN DATE(start_at) <= VAULT_TODAY() THEN 1 ELSE 0 END AS has_started
|
|
1110
|
+
FROM _v_us_events
|
|
1111
|
+
WHERE
|
|
1112
|
+
kind = 'meetup';
|
|
1113
|
+
|
|
1114
|
+
/* _v_us_meet_days */
|
|
1115
|
+
/* _v_us_meet_days */
|
|
1116
|
+
CREATE VIEW _v_us_meet_days AS
|
|
1117
|
+
SELECT
|
|
1118
|
+
d.meet_on,
|
|
1119
|
+
COUNT(*) AS event_count
|
|
1120
|
+
FROM v_meet_days AS d
|
|
1121
|
+
CROSS JOIN JSON_EACH(d.event_ids_json) AS j
|
|
1122
|
+
JOIN v_us_meetups AS m
|
|
1123
|
+
ON m.id = j."value"
|
|
1124
|
+
GROUP BY
|
|
1125
|
+
d.meet_on;
|
|
1126
|
+
|
|
1127
|
+
/* _v_us_partner_start */
|
|
1128
|
+
/* _v_us_partner_start */
|
|
1129
|
+
CREATE VIEW _v_us_partner_start AS
|
|
1130
|
+
WITH relations_current AS (
|
|
1131
|
+
SELECT
|
|
1132
|
+
r.*
|
|
1133
|
+
FROM relations AS r
|
|
1134
|
+
JOIN _v_module_people AS a
|
|
1135
|
+
ON a.id = r.from_person_id
|
|
1136
|
+
JOIN _v_module_people AS b
|
|
1137
|
+
ON b.id = r.to_person_id
|
|
1138
|
+
LEFT JOIN _v_annotations AS label
|
|
1139
|
+
ON label.target_type = 'relations' AND label.target_key = r.id
|
|
1140
|
+
WHERE
|
|
1141
|
+
r.relation = 'partner'
|
|
1142
|
+
AND a.module <> b.module
|
|
1143
|
+
AND r.status = 'confirmed'
|
|
1144
|
+
AND r.deleted_at IS NULL
|
|
1145
|
+
AND COALESCE(label.hidden, 0) = 0
|
|
1146
|
+
AND (
|
|
1147
|
+
r.until_on IS NULL OR r.until_on >= VAULT_TODAY()
|
|
1148
|
+
)
|
|
1149
|
+
AND (
|
|
1150
|
+
r.since_on IS NULL OR r.since_on <= VAULT_TODAY()
|
|
1151
|
+
)
|
|
1152
|
+
)
|
|
1153
|
+
SELECT
|
|
1154
|
+
CASE
|
|
1155
|
+
WHEN COUNT(DISTINCT since_on) = 1 AND DATE(MIN(since_on)) = MIN(since_on)
|
|
1156
|
+
THEN MIN(since_on)
|
|
1157
|
+
END AS since_on,
|
|
1158
|
+
CASE
|
|
1159
|
+
WHEN COUNT(DISTINCT since_on) > 1
|
|
1160
|
+
THEN 'conflicting_start_dates'
|
|
1161
|
+
WHEN COUNT(DISTINCT since_on) = 1 AND DATE(MIN(since_on)) = MIN(since_on)
|
|
1162
|
+
THEN 'confirmed_partner_relation'
|
|
1163
|
+
ELSE 'no_confirmed_start'
|
|
1164
|
+
END AS basis
|
|
1165
|
+
FROM relations_current;
|
|
1166
|
+
|
|
1167
|
+
/* _v_us_reply_samples */
|
|
1168
|
+
/* _v_us_reply_samples */
|
|
1169
|
+
CREATE VIEW _v_us_reply_samples AS
|
|
1170
|
+
WITH preceding AS (
|
|
1171
|
+
SELECT
|
|
1172
|
+
*,
|
|
1173
|
+
LAG(sender_module) OVER history AS previous_sender,
|
|
1174
|
+
LAG(end_ts) OVER history AS previous_end_ts,
|
|
1175
|
+
LAG(timing_ambiguous) OVER history AS previous_ambiguous
|
|
1176
|
+
FROM _v_us_chat_turns
|
|
1177
|
+
WINDOW history AS (PARTITION BY session_key ORDER BY turn_no NULLS FIRST)
|
|
1178
|
+
), samples AS (
|
|
1179
|
+
SELECT
|
|
1180
|
+
*,
|
|
1181
|
+
start_ts - previous_end_ts AS reply_seconds,
|
|
1182
|
+
COALESCE(
|
|
1183
|
+
(
|
|
1184
|
+
SELECT
|
|
1185
|
+
CASE
|
|
1186
|
+
WHEN JSON_TYPE(value_json) = 'integer'
|
|
1187
|
+
THEN GREATEST(1, LEAST(604800, CAST(value_json AS BIGINT)))
|
|
1188
|
+
END
|
|
1189
|
+
FROM settings
|
|
1190
|
+
WHERE
|
|
1191
|
+
key = 'reply_max_gap_seconds'
|
|
1192
|
+
),
|
|
1193
|
+
86400
|
|
1194
|
+
) AS max_gap
|
|
1195
|
+
FROM preceding
|
|
1196
|
+
WHERE
|
|
1197
|
+
NOT sender_module IS NULL
|
|
1198
|
+
AND NOT previous_sender IS NULL
|
|
1199
|
+
AND sender_module <> previous_sender
|
|
1200
|
+
AND timing_ambiguous = 0
|
|
1201
|
+
AND previous_ambiguous = 0
|
|
1202
|
+
)
|
|
1203
|
+
SELECT
|
|
1204
|
+
*,
|
|
1205
|
+
CASE WHEN reply_seconds BETWEEN 0 AND max_gap THEN 1 ELSE 0 END AS usable
|
|
1206
|
+
FROM samples;
|
|
1207
|
+
|
|
1208
|
+
/* _v_us_reply_daily */
|
|
1209
|
+
/* _v_us_reply_daily */
|
|
1210
|
+
CREATE VIEW _v_us_reply_daily AS
|
|
1211
|
+
WITH usable AS (
|
|
1212
|
+
SELECT
|
|
1213
|
+
*,
|
|
1214
|
+
SUBSTRING(started_at FROM 1 FOR 10) AS reply_on,
|
|
1215
|
+
ROW_NUMBER() OVER (
|
|
1216
|
+
PARTITION BY SUBSTRING(started_at FROM 1 FOR 10), sender_module
|
|
1217
|
+
ORDER BY reply_seconds NULLS FIRST, session_key NULLS FIRST, turn_no NULLS FIRST
|
|
1218
|
+
) AS position,
|
|
1219
|
+
COUNT(*) OVER (PARTITION BY SUBSTRING(started_at FROM 1 FOR 10), sender_module) AS samples
|
|
1220
|
+
FROM _v_us_reply_samples
|
|
1221
|
+
WHERE
|
|
1222
|
+
usable = 1
|
|
1223
|
+
), stats AS (
|
|
1224
|
+
SELECT
|
|
1225
|
+
reply_on,
|
|
1226
|
+
sender_module,
|
|
1227
|
+
COUNT(*) AS reply_count,
|
|
1228
|
+
AVG(reply_seconds) AS avg_reply_seconds,
|
|
1229
|
+
AVG(
|
|
1230
|
+
CASE
|
|
1231
|
+
WHEN position IN ((
|
|
1232
|
+
samples + 1
|
|
1233
|
+
) / NULLIF(2, 0), (
|
|
1234
|
+
samples + 2
|
|
1235
|
+
) / NULLIF(2, 0))
|
|
1236
|
+
THEN reply_seconds
|
|
1237
|
+
END
|
|
1238
|
+
) AS median_reply_seconds,
|
|
1239
|
+
MIN(reply_seconds) AS min_reply_seconds,
|
|
1240
|
+
MAX(reply_seconds) AS max_reply_seconds
|
|
1241
|
+
FROM usable
|
|
1242
|
+
GROUP BY
|
|
1243
|
+
reply_on,
|
|
1244
|
+
sender_module
|
|
1245
|
+
), days AS (
|
|
1246
|
+
SELECT
|
|
1247
|
+
SUBSTRING(started_at FROM 1 FOR 10) AS reply_on,
|
|
1248
|
+
sender_module,
|
|
1249
|
+
SUM(CAST(usable = 0 AS INT)) AS long_gap_count
|
|
1250
|
+
FROM _v_us_reply_samples
|
|
1251
|
+
GROUP BY
|
|
1252
|
+
SUBSTRING(started_at FROM 1 FOR 10),
|
|
1253
|
+
sender_module
|
|
1254
|
+
)
|
|
1255
|
+
SELECT
|
|
1256
|
+
d.reply_on,
|
|
1257
|
+
d.sender_module,
|
|
1258
|
+
COALESCE(s.reply_count, 0) AS reply_count,
|
|
1259
|
+
s.avg_reply_seconds,
|
|
1260
|
+
s.median_reply_seconds,
|
|
1261
|
+
s.min_reply_seconds,
|
|
1262
|
+
s.max_reply_seconds,
|
|
1263
|
+
d.long_gap_count
|
|
1264
|
+
FROM days AS d
|
|
1265
|
+
LEFT JOIN stats AS s
|
|
1266
|
+
ON s.reply_on = d.reply_on AND s.sender_module = d.sender_module;
|
|
1267
|
+
|
|
1268
|
+
/* v_us_conflicts */
|
|
1269
|
+
/* v_us_conflicts */
|
|
1270
|
+
CREATE VIEW v_us_conflicts AS
|
|
1271
|
+
SELECT
|
|
1272
|
+
*
|
|
1273
|
+
FROM _v_us_events
|
|
1274
|
+
WHERE
|
|
1275
|
+
kind = 'conflict';
|
|
1276
|
+
|
|
1277
|
+
/* q_conflicts_total */
|
|
1278
|
+
/* q_conflicts_total */
|
|
1279
|
+
CREATE VIEW q_conflicts_total AS
|
|
1280
|
+
SELECT
|
|
1281
|
+
COUNT(*) AS "value"
|
|
1282
|
+
FROM v_us_conflicts
|
|
1283
|
+
WHERE
|
|
1284
|
+
DATE(start_at) <= VAULT_TODAY();
|
|
1285
|
+
|
|
1286
|
+
/* q_days_together */
|
|
1287
|
+
/* q_days_together */
|
|
1288
|
+
CREATE VIEW q_days_together AS
|
|
1289
|
+
SELECT
|
|
1290
|
+
CASE
|
|
1291
|
+
WHEN NOT since_on IS NULL
|
|
1292
|
+
THEN CAST(TRUNC(JULIANDAY(VAULT_TODAY()) - JULIANDAY(since_on)) AS BIGINT) + 1
|
|
1293
|
+
END AS "value",
|
|
1294
|
+
since_on,
|
|
1295
|
+
basis
|
|
1296
|
+
FROM _v_us_partner_start;
|
|
1297
|
+
|
|
1298
|
+
/* q_first_meet_on */
|
|
1299
|
+
/* q_first_meet_on */
|
|
1300
|
+
CREATE VIEW q_first_meet_on AS
|
|
1301
|
+
SELECT
|
|
1302
|
+
MIN(DATE(start_at)) AS "value"
|
|
1303
|
+
FROM v_us_meetups
|
|
1304
|
+
WHERE
|
|
1305
|
+
has_started = 1;
|
|
1306
|
+
|
|
1307
|
+
/* v_us_gifts */
|
|
1308
|
+
/* v_us_gifts */
|
|
1309
|
+
CREATE VIEW v_us_gifts AS
|
|
1310
|
+
SELECT
|
|
1311
|
+
'gift_ideas' AS source_type,
|
|
1312
|
+
g.id AS source_key,
|
|
1313
|
+
g.title,
|
|
1314
|
+
g.for_person_id,
|
|
1315
|
+
g.given_event_id AS event_id,
|
|
1316
|
+
e.start_at AS given_at,
|
|
1317
|
+
c.amount_cents,
|
|
1318
|
+
c.amount_cents / NULLIF(100.0, 0) AS amount_yuan,
|
|
1319
|
+
g.budget_cents,
|
|
1320
|
+
COALESCE(c.unknown_amount_count, 1) AS unknown_amount_count,
|
|
1321
|
+
COALESCE(c.pending_match_count, 0) AS pending_match_count,
|
|
1322
|
+
COALESCE(c.estimated_count, 0) AS estimated_count,
|
|
1323
|
+
CASE
|
|
1324
|
+
WHEN COALESCE(a.sensitivity, g.sensitivity) = 'intimate'
|
|
1325
|
+
OR e.effective_sensitivity = 'intimate'
|
|
1326
|
+
OR c.sensitivity = 'intimate'
|
|
1327
|
+
THEN 'intimate'
|
|
1328
|
+
WHEN NOT c.source_key IS NULL
|
|
1329
|
+
OR COALESCE(a.sensitivity, g.sensitivity) = 'private'
|
|
1330
|
+
OR e.effective_sensitivity = 'private'
|
|
1331
|
+
THEN 'private'
|
|
1332
|
+
ELSE 'normal'
|
|
1333
|
+
END AS sensitivity
|
|
1334
|
+
FROM gift_ideas AS g
|
|
1335
|
+
JOIN _v_module_people AS recipient
|
|
1336
|
+
ON recipient.id = g.for_person_id
|
|
1337
|
+
LEFT JOIN _v_us_events AS e
|
|
1338
|
+
ON e.id = g.given_event_id
|
|
1339
|
+
LEFT JOIN _v_purchase_costs AS c
|
|
1340
|
+
ON c.source_type = g.purchase_type AND c.source_key = g.purchase_key
|
|
1341
|
+
LEFT JOIN _v_annotations AS a
|
|
1342
|
+
ON a.target_type = 'gift_ideas' AND a.target_key = g.id
|
|
1343
|
+
WHERE
|
|
1344
|
+
g.deleted_at IS NULL
|
|
1345
|
+
AND g.status = 'confirmed'
|
|
1346
|
+
AND g.idea_state = 'given'
|
|
1347
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
1348
|
+
AND (
|
|
1349
|
+
g.given_event_id IS NULL OR NOT e.id IS NULL
|
|
1350
|
+
)
|
|
1351
|
+
UNION ALL
|
|
1352
|
+
SELECT
|
|
1353
|
+
'events',
|
|
1354
|
+
e.id,
|
|
1355
|
+
e.title,
|
|
1356
|
+
NULL,
|
|
1357
|
+
e.id,
|
|
1358
|
+
e.start_at,
|
|
1359
|
+
NULL,
|
|
1360
|
+
NULL,
|
|
1361
|
+
NULL,
|
|
1362
|
+
1,
|
|
1363
|
+
0,
|
|
1364
|
+
0,
|
|
1365
|
+
e.effective_sensitivity
|
|
1366
|
+
FROM _v_us_events AS e
|
|
1367
|
+
WHERE
|
|
1368
|
+
e.kind = 'gift'
|
|
1369
|
+
AND NOT EXISTS(
|
|
1370
|
+
SELECT
|
|
1371
|
+
1
|
|
1372
|
+
FROM gift_ideas AS g
|
|
1373
|
+
WHERE
|
|
1374
|
+
g.given_event_id = e.id AND g.idea_state = 'given'
|
|
1375
|
+
);
|
|
1376
|
+
|
|
1377
|
+
/* q_gifts_given_total */
|
|
1378
|
+
/* q_gifts_given_total */
|
|
1379
|
+
CREATE VIEW q_gifts_given_total AS
|
|
1380
|
+
SELECT
|
|
1381
|
+
COUNT(*) AS "value",
|
|
1382
|
+
COALESCE(SUM(CAST(given_at IS NULL AS INT)), 0) AS undated_count
|
|
1383
|
+
FROM v_us_gifts;
|
|
1384
|
+
|
|
1385
|
+
/* v_us_interaction_daily */
|
|
1386
|
+
/* v_us_interaction_daily */
|
|
1387
|
+
CREATE VIEW v_us_interaction_daily AS
|
|
1388
|
+
WITH messages AS MATERIALIZED (
|
|
1389
|
+
SELECT
|
|
1390
|
+
*
|
|
1391
|
+
FROM _v_us_messages
|
|
1392
|
+
WHERE
|
|
1393
|
+
hidden = 0
|
|
1394
|
+
), days AS (
|
|
1395
|
+
SELECT
|
|
1396
|
+
SUBSTRING(sent_at FROM 1 FOR 10) AS chat_on,
|
|
1397
|
+
COUNT(*) AS message_count,
|
|
1398
|
+
SUM(CAST(sender_module = 'me' AS INT)) AS me_count,
|
|
1399
|
+
SUM(CAST(sender_module = 'her' AS INT)) AS her_count,
|
|
1400
|
+
SUM(CAST(sender_module IS NULL AS INT)) AS unidentified_count,
|
|
1401
|
+
MIN(sent_ts) AS first_ts,
|
|
1402
|
+
COUNT(DISTINCT session_key) AS session_count
|
|
1403
|
+
FROM messages
|
|
1404
|
+
GROUP BY
|
|
1405
|
+
SUBSTRING(sent_at FROM 1 FOR 10)
|
|
1406
|
+
), first_senders AS (
|
|
1407
|
+
SELECT
|
|
1408
|
+
d.chat_on,
|
|
1409
|
+
CASE
|
|
1410
|
+
WHEN COUNT(DISTINCT m.sender_module) = 1 AND COUNT(*) = COUNT(m.sender_module)
|
|
1411
|
+
THEN MIN(m.person_id)
|
|
1412
|
+
END AS first_sender_id
|
|
1413
|
+
FROM days AS d
|
|
1414
|
+
JOIN messages AS m
|
|
1415
|
+
ON SUBSTRING(m.sent_at FROM 1 FOR 10) = d.chat_on AND m.sent_ts = d.first_ts
|
|
1416
|
+
GROUP BY
|
|
1417
|
+
d.chat_on
|
|
1418
|
+
)
|
|
1419
|
+
SELECT
|
|
1420
|
+
d.chat_on,
|
|
1421
|
+
d.message_count,
|
|
1422
|
+
COALESCE(d.me_count, 0) AS me_count,
|
|
1423
|
+
COALESCE(d.her_count, 0) AS her_count,
|
|
1424
|
+
d.unidentified_count,
|
|
1425
|
+
d.session_count,
|
|
1426
|
+
f.first_sender_id,
|
|
1427
|
+
COALESCE(me.reply_count, 0) AS me_reply_count,
|
|
1428
|
+
me.avg_reply_seconds AS me_avg_reply_seconds,
|
|
1429
|
+
COALESCE(her.reply_count, 0) AS her_reply_count,
|
|
1430
|
+
her.avg_reply_seconds AS her_avg_reply_seconds,
|
|
1431
|
+
COALESCE(me.long_gap_count, 0) + COALESCE(her.long_gap_count, 0) AS long_gap_count
|
|
1432
|
+
FROM days AS d
|
|
1433
|
+
JOIN first_senders AS f
|
|
1434
|
+
ON f.chat_on = d.chat_on
|
|
1435
|
+
LEFT JOIN _v_us_reply_daily AS me
|
|
1436
|
+
ON me.reply_on = d.chat_on AND me.sender_module = 'me'
|
|
1437
|
+
LEFT JOIN _v_us_reply_daily AS her
|
|
1438
|
+
ON her.reply_on = d.chat_on AND her.sender_module = 'her';
|
|
1439
|
+
|
|
1440
|
+
/* q_interaction_daily */
|
|
1441
|
+
/* q_interaction_daily */
|
|
1442
|
+
CREATE VIEW q_interaction_daily AS
|
|
1443
|
+
SELECT
|
|
1444
|
+
*
|
|
1445
|
+
FROM v_us_interaction_daily;
|
|
1446
|
+
|
|
1447
|
+
/* q_last_meet_on */
|
|
1448
|
+
/* q_last_meet_on */
|
|
1449
|
+
CREATE VIEW q_last_meet_on AS
|
|
1450
|
+
SELECT
|
|
1451
|
+
MAX(meet_on) AS "value"
|
|
1452
|
+
FROM _v_us_meet_days
|
|
1453
|
+
WHERE
|
|
1454
|
+
meet_on <= VAULT_TODAY();
|
|
1455
|
+
|
|
1456
|
+
/* q_meet_days_total */
|
|
1457
|
+
/* q_meet_days_total */
|
|
1458
|
+
CREATE VIEW q_meet_days_total AS
|
|
1459
|
+
SELECT
|
|
1460
|
+
COUNT(*) AS "value"
|
|
1461
|
+
FROM _v_us_meet_days
|
|
1462
|
+
WHERE
|
|
1463
|
+
meet_on <= VAULT_TODAY();
|
|
1464
|
+
|
|
1465
|
+
/* q_meetups_total */
|
|
1466
|
+
/* q_meetups_total */
|
|
1467
|
+
CREATE VIEW q_meetups_total AS
|
|
1468
|
+
SELECT
|
|
1469
|
+
COUNT(*) AS "value"
|
|
1470
|
+
FROM v_us_meetups
|
|
1471
|
+
WHERE
|
|
1472
|
+
has_started = 1;
|
|
1473
|
+
|
|
1474
|
+
/* v_money_flow */
|
|
1475
|
+
/* v_money_flow */
|
|
1476
|
+
CREATE VIEW v_money_flow AS
|
|
1477
|
+
WITH participants AS (
|
|
1478
|
+
SELECT
|
|
1479
|
+
p.*,
|
|
1480
|
+
me.id AS self_id,
|
|
1481
|
+
partner.id AS partner_id,
|
|
1482
|
+
CASE WHEN p.direction = 'expense' THEN me.id ELSE partner.id END AS from_person_id,
|
|
1483
|
+
CASE WHEN p.direction = 'expense' THEN partner.id ELSE me.id END AS to_person_id
|
|
1484
|
+
FROM v_payments AS p
|
|
1485
|
+
JOIN persons AS me
|
|
1486
|
+
ON me.is_self = 1 AND me.deleted_at IS NULL
|
|
1487
|
+
JOIN persons AS partner
|
|
1488
|
+
ON partner.is_default = 1 AND partner.deleted_at IS NULL
|
|
1489
|
+
WHERE
|
|
1490
|
+
p.counterparty_person_id = partner.id
|
|
1491
|
+
AND p.is_transfer = 1
|
|
1492
|
+
AND p.is_refund = 0
|
|
1493
|
+
AND p.direction IN ('expense', 'income')
|
|
1494
|
+
AND p.hidden = 0
|
|
1495
|
+
AND p.net_amount_cents > 0
|
|
1496
|
+
)
|
|
1497
|
+
SELECT
|
|
1498
|
+
SUBSTRING(occurred_at FROM 1 FOR 10) AS flow_on,
|
|
1499
|
+
from_person_id,
|
|
1500
|
+
to_person_id,
|
|
1501
|
+
COUNT(*) AS transfer_count,
|
|
1502
|
+
SUM(amount_cents) AS gross_cents,
|
|
1503
|
+
SUM(effective_refund_cents) AS refund_cents,
|
|
1504
|
+
SUM(net_amount_cents) AS amount_cents,
|
|
1505
|
+
SUM(net_amount_cents) / NULLIF(100.0, 0) AS amount_yuan
|
|
1506
|
+
FROM participants
|
|
1507
|
+
GROUP BY
|
|
1508
|
+
SUBSTRING(occurred_at FROM 1 FOR 10),
|
|
1509
|
+
from_person_id,
|
|
1510
|
+
to_person_id;
|
|
1511
|
+
|
|
1512
|
+
/* v_us_money_flow */
|
|
1513
|
+
/* v_us_money_flow */
|
|
1514
|
+
CREATE VIEW v_us_money_flow AS
|
|
1515
|
+
SELECT
|
|
1516
|
+
*
|
|
1517
|
+
FROM v_money_flow;
|
|
1518
|
+
|
|
1519
|
+
/* q_money_received_total */
|
|
1520
|
+
/* q_money_received_total */
|
|
1521
|
+
CREATE VIEW q_money_received_total AS
|
|
1522
|
+
SELECT
|
|
1523
|
+
COALESCE(SUM(f.amount_cents), 0) / NULLIF(100.0, 0) AS "value",
|
|
1524
|
+
COALESCE(SUM(f.amount_cents), 0) AS amount_cents
|
|
1525
|
+
FROM v_us_money_flow AS f
|
|
1526
|
+
JOIN _v_module_people AS p
|
|
1527
|
+
ON p.id = f.to_person_id AND p.module = 'me';
|
|
1528
|
+
|
|
1529
|
+
/* q_money_sent_total */
|
|
1530
|
+
/* q_money_sent_total */
|
|
1531
|
+
CREATE VIEW q_money_sent_total AS
|
|
1532
|
+
SELECT
|
|
1533
|
+
COALESCE(SUM(f.amount_cents), 0) / NULLIF(100.0, 0) AS "value",
|
|
1534
|
+
COALESCE(SUM(f.amount_cents), 0) AS amount_cents
|
|
1535
|
+
FROM v_us_money_flow AS f
|
|
1536
|
+
JOIN _v_module_people AS p
|
|
1537
|
+
ON p.id = f.from_person_id AND p.module = 'me';
|
|
1538
|
+
|
|
1539
|
+
/* q_next_meetup_on */
|
|
1540
|
+
/* q_next_meetup_on */
|
|
1541
|
+
CREATE VIEW q_next_meetup_on AS
|
|
1542
|
+
SELECT
|
|
1543
|
+
MIN(DATE(start_at)) AS "value"
|
|
1544
|
+
FROM v_us_meetups
|
|
1545
|
+
WHERE
|
|
1546
|
+
DATE(start_at) >= VAULT_TODAY();
|
|
1547
|
+
|
|
1548
|
+
/* v_promises */
|
|
1549
|
+
/* v_promises */
|
|
1550
|
+
CREATE VIEW v_promises AS
|
|
1551
|
+
WITH checks AS (
|
|
1552
|
+
SELECT
|
|
1553
|
+
c.promise_id,
|
|
1554
|
+
SUM(CAST(c.result = 'broken' AS INT)) AS broken_count,
|
|
1555
|
+
COUNT(*) AS check_count,
|
|
1556
|
+
MAX(c.checked_on) AS last_checked_on
|
|
1557
|
+
FROM promise_checks AS c
|
|
1558
|
+
LEFT JOIN _v_annotations AS a
|
|
1559
|
+
ON a.target_type = 'promise_checks' AND a.target_key = c.id
|
|
1560
|
+
WHERE
|
|
1561
|
+
c.deleted_at IS NULL AND c.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
1562
|
+
GROUP BY
|
|
1563
|
+
c.promise_id
|
|
1564
|
+
)
|
|
1565
|
+
SELECT
|
|
1566
|
+
p.*,
|
|
1567
|
+
COALESCE(c.broken_count, 0) AS broken_count,
|
|
1568
|
+
COALESCE(c.check_count, 0) AS check_count,
|
|
1569
|
+
c.last_checked_on,
|
|
1570
|
+
CASE
|
|
1571
|
+
WHEN p.due_on < VAULT_TODAY() AND p.fulfillment IN ('active', 'pending')
|
|
1572
|
+
THEN 1
|
|
1573
|
+
ELSE 0
|
|
1574
|
+
END AS overdue
|
|
1575
|
+
FROM promises AS p
|
|
1576
|
+
LEFT JOIN checks AS c
|
|
1577
|
+
ON c.promise_id = p.id
|
|
1578
|
+
LEFT JOIN _v_annotations AS a
|
|
1579
|
+
ON a.target_type = 'promises' AND a.target_key = p.id
|
|
1580
|
+
WHERE
|
|
1581
|
+
p.deleted_at IS NULL AND p.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0;
|
|
1582
|
+
|
|
1583
|
+
/* v_me_promises */
|
|
1584
|
+
/* v_me_promises */
|
|
1585
|
+
CREATE VIEW v_me_promises AS
|
|
1586
|
+
SELECT
|
|
1587
|
+
p.*
|
|
1588
|
+
FROM v_promises AS p
|
|
1589
|
+
JOIN _v_module_people AS me
|
|
1590
|
+
ON me.module = 'me'
|
|
1591
|
+
WHERE
|
|
1592
|
+
p.promiser_id = me.id OR (
|
|
1593
|
+
p.is_mutual = 1 AND p.recipient_id = me.id
|
|
1594
|
+
);
|
|
1595
|
+
|
|
1596
|
+
/* q_promises_open */
|
|
1597
|
+
/* q_promises_open */
|
|
1598
|
+
CREATE VIEW q_promises_open AS
|
|
1599
|
+
SELECT
|
|
1600
|
+
COUNT(*) AS "value",
|
|
1601
|
+
COALESCE(SUM(overdue), 0) AS overdue_count
|
|
1602
|
+
FROM v_me_promises
|
|
1603
|
+
WHERE
|
|
1604
|
+
fulfillment IN ('active', 'pending');
|
|
1605
|
+
|
|
1606
|
+
/* v_us_spending_by_beneficiary */
|
|
1607
|
+
/* v_us_spending_by_beneficiary */
|
|
1608
|
+
CREATE VIEW v_us_spending_by_beneficiary AS
|
|
1609
|
+
SELECT
|
|
1610
|
+
s.source_type,
|
|
1611
|
+
s.source_key,
|
|
1612
|
+
s.platform,
|
|
1613
|
+
s.occurred_at,
|
|
1614
|
+
s.category,
|
|
1615
|
+
s.title,
|
|
1616
|
+
s.sensitivity,
|
|
1617
|
+
b.person_id,
|
|
1618
|
+
b.ratio AS share_ratio,
|
|
1619
|
+
b.allocated_cents AS amount_cents,
|
|
1620
|
+
b.allocated_cents / NULLIF(100.0, 0) AS amount_yuan,
|
|
1621
|
+
s.amount_is_estimated,
|
|
1622
|
+
s.needs_match_review
|
|
1623
|
+
FROM v_spending AS s
|
|
1624
|
+
JOIN _v_spending_share_rows AS b
|
|
1625
|
+
ON b.source_type = s.source_type AND b.source_key = s.source_key
|
|
1626
|
+
WHERE
|
|
1627
|
+
b.invalid_shares = 0 AND b.ratio > 0;
|
|
1628
|
+
|
|
1629
|
+
/* q_spend_on_her_yearly */
|
|
1630
|
+
/* q_spend_on_her_yearly */
|
|
1631
|
+
CREATE VIEW q_spend_on_her_yearly AS
|
|
1632
|
+
WITH years AS (
|
|
1633
|
+
SELECT DISTINCT
|
|
1634
|
+
SUBSTRING(occurred_at FROM 1 FOR 4) AS year
|
|
1635
|
+
FROM v_spending
|
|
1636
|
+
), amounts AS (
|
|
1637
|
+
SELECT
|
|
1638
|
+
SUBSTRING(s.occurred_at FROM 1 FOR 4) AS year,
|
|
1639
|
+
SUM(s.amount_cents) AS amount_cents
|
|
1640
|
+
FROM v_us_spending_by_beneficiary AS s
|
|
1641
|
+
JOIN _v_module_people AS p
|
|
1642
|
+
ON p.id = s.person_id AND p.module = 'her'
|
|
1643
|
+
WHERE
|
|
1644
|
+
s.needs_match_review = 0
|
|
1645
|
+
GROUP BY
|
|
1646
|
+
SUBSTRING(s.occurred_at FROM 1 FOR 4)
|
|
1647
|
+
), quality AS (
|
|
1648
|
+
SELECT
|
|
1649
|
+
SUBSTRING(occurred_at FROM 1 FOR 4) AS year,
|
|
1650
|
+
SUM(CAST(amount_cents IS NULL AS INT)) AS unknown_amount_count,
|
|
1651
|
+
SUM(CAST(amount_is_estimated = 1 AND NOT amount_cents IS NULL AS INT)) AS estimated_count,
|
|
1652
|
+
SUM(needs_match_review) AS pending_match_count,
|
|
1653
|
+
SUM(CASE WHEN unassigned_cents > 0 OR invalid_shares = 1 THEN 1 ELSE 0 END) AS unassigned_count
|
|
1654
|
+
FROM _v_spending_quality
|
|
1655
|
+
GROUP BY
|
|
1656
|
+
SUBSTRING(occurred_at FROM 1 FOR 4)
|
|
1657
|
+
)
|
|
1658
|
+
SELECT
|
|
1659
|
+
y.year,
|
|
1660
|
+
COALESCE(a.amount_cents, 0) AS amount_cents,
|
|
1661
|
+
COALESCE(a.amount_cents, 0) / NULLIF(100.0, 0) AS amount_yuan,
|
|
1662
|
+
q.unknown_amount_count,
|
|
1663
|
+
q.estimated_count,
|
|
1664
|
+
q.pending_match_count,
|
|
1665
|
+
q.unassigned_count
|
|
1666
|
+
FROM years AS y
|
|
1667
|
+
LEFT JOIN amounts AS a
|
|
1668
|
+
ON a.year IS NOT DISTINCT FROM y.year
|
|
1669
|
+
JOIN quality AS q
|
|
1670
|
+
ON q.year IS NOT DISTINCT FROM y.year;
|
|
1671
|
+
|
|
1672
|
+
/* q_spend_on_her_2026 */
|
|
1673
|
+
/* q_spend_on_her_2026 */
|
|
1674
|
+
CREATE VIEW q_spend_on_her_2026 AS
|
|
1675
|
+
SELECT
|
|
1676
|
+
COALESCE(SUM(amount_cents), 0) / NULLIF(100.0, 0) AS "value",
|
|
1677
|
+
COALESCE(SUM(amount_cents), 0) AS amount_cents,
|
|
1678
|
+
COALESCE(SUM(unknown_amount_count), 0) AS unknown_amount_count,
|
|
1679
|
+
COALESCE(SUM(estimated_count), 0) AS estimated_count,
|
|
1680
|
+
COALESCE(SUM(pending_match_count), 0) AS pending_match_count,
|
|
1681
|
+
COALESCE(SUM(unassigned_count), 0) AS unassigned_count,
|
|
1682
|
+
(
|
|
1683
|
+
SELECT
|
|
1684
|
+
COUNT(*)
|
|
1685
|
+
FROM v_spending
|
|
1686
|
+
WHERE
|
|
1687
|
+
occurred_at IS NULL
|
|
1688
|
+
) AS undated_count
|
|
1689
|
+
FROM q_spend_on_her_yearly
|
|
1690
|
+
WHERE
|
|
1691
|
+
year = '2026';
|
|
1692
|
+
|
|
1693
|
+
/* q_spend_on_her_total */
|
|
1694
|
+
/* q_spend_on_her_total */
|
|
1695
|
+
CREATE VIEW q_spend_on_her_total AS
|
|
1696
|
+
SELECT
|
|
1697
|
+
COALESCE(SUM(amount_cents), 0) / NULLIF(100.0, 0) AS "value",
|
|
1698
|
+
COALESCE(SUM(amount_cents), 0) AS amount_cents,
|
|
1699
|
+
COALESCE(SUM(unknown_amount_count), 0) AS unknown_amount_count,
|
|
1700
|
+
COALESCE(SUM(estimated_count), 0) AS estimated_count,
|
|
1701
|
+
COALESCE(SUM(pending_match_count), 0) AS pending_match_count,
|
|
1702
|
+
COALESCE(SUM(unassigned_count), 0) AS unassigned_count
|
|
1703
|
+
FROM q_spend_on_her_yearly;
|
|
1704
|
+
|
|
1705
|
+
/* v_me_spending_monthly */
|
|
1706
|
+
/* v_me_spending_monthly */
|
|
1707
|
+
CREATE VIEW v_me_spending_monthly AS
|
|
1708
|
+
SELECT
|
|
1709
|
+
SUBSTRING(occurred_at FROM 1 FOR 7) AS month,
|
|
1710
|
+
platform,
|
|
1711
|
+
COALESCE(category, '未分类') AS category,
|
|
1712
|
+
COUNT(*) AS row_count,
|
|
1713
|
+
SUM(CASE WHEN needs_match_review = 0 THEN amount_cents END) AS amount_cents,
|
|
1714
|
+
SUM(CASE WHEN needs_match_review = 0 THEN amount_cents END) / NULLIF(100.0, 0) AS amount_yuan,
|
|
1715
|
+
SUM(
|
|
1716
|
+
CASE WHEN needs_match_review = 0 AND amount_is_estimated = 0 THEN amount_cents END
|
|
1717
|
+
) AS exact_cents,
|
|
1718
|
+
SUM(
|
|
1719
|
+
CASE
|
|
1720
|
+
WHEN needs_match_review = 0 AND amount_is_estimated = 1
|
|
1721
|
+
THEN amount_cents
|
|
1722
|
+
ELSE 0
|
|
1723
|
+
END
|
|
1724
|
+
) AS estimated_cents,
|
|
1725
|
+
SUM(CASE WHEN needs_match_review = 0 THEN unassigned_cents ELSE 0 END) AS unassigned_cents,
|
|
1726
|
+
SUM(CAST(amount_cents IS NULL AS INT)) AS unknown_amount_count,
|
|
1727
|
+
SUM(needs_match_review) AS pending_match_count,
|
|
1728
|
+
SUM(CASE WHEN needs_match_review = 1 THEN amount_cents ELSE 0 END) AS pending_candidate_cents,
|
|
1729
|
+
SUM(invalid_shares) AS invalid_shares_count
|
|
1730
|
+
FROM _v_spending_quality
|
|
1731
|
+
GROUP BY
|
|
1732
|
+
SUBSTRING(occurred_at FROM 1 FOR 7),
|
|
1733
|
+
platform,
|
|
1734
|
+
COALESCE(category, '未分类');
|
|
1735
|
+
|
|
1736
|
+
/* q_spending_monthly */
|
|
1737
|
+
/* q_spending_monthly */
|
|
1738
|
+
CREATE VIEW q_spending_monthly AS
|
|
1739
|
+
SELECT
|
|
1740
|
+
*
|
|
1741
|
+
FROM v_me_spending_monthly;
|
|
1742
|
+
|
|
1743
|
+
/* v_upcoming_dates */
|
|
1744
|
+
/* v_upcoming_dates */
|
|
1745
|
+
CREATE VIEW v_upcoming_dates AS
|
|
1746
|
+
WITH next_dates AS (
|
|
1747
|
+
SELECT
|
|
1748
|
+
id,
|
|
1749
|
+
MIN(occurs_on) AS next_on
|
|
1750
|
+
FROM _v_date_occurrences
|
|
1751
|
+
WHERE
|
|
1752
|
+
occurs_on >= VAULT_TODAY()
|
|
1753
|
+
AND occurs_on >= COALESCE(first_on, VAULT_CALENDAR_DATE(calendar, date_value, NULL), occurs_on)
|
|
1754
|
+
GROUP BY
|
|
1755
|
+
id
|
|
1756
|
+
), computed AS (
|
|
1757
|
+
SELECT
|
|
1758
|
+
d.*,
|
|
1759
|
+
n.next_on,
|
|
1760
|
+
CAST(TRUNC(JULIANDAY(n.next_on) - JULIANDAY(VAULT_TODAY())) AS BIGINT) AS days_until,
|
|
1761
|
+
CASE
|
|
1762
|
+
WHEN NOT d.first_on IS NULL AND d.calendar = 'lunar'
|
|
1763
|
+
THEN VAULT_LUNAR_YEAR(n.next_on) - VAULT_LUNAR_YEAR(d.first_on)
|
|
1764
|
+
WHEN NOT d.first_on IS NULL
|
|
1765
|
+
THEN CAST(SUBSTRING(n.next_on FROM 1 FOR 4) AS BIGINT) - CAST(SUBSTRING(d.first_on FROM 1 FOR 4) AS BIGINT)
|
|
1766
|
+
END AS anniversary_no,
|
|
1767
|
+
DATE(n.next_on, PRINTF('-%d days', COALESCE(d.remind_days_before, 0))) AS remind_on
|
|
1768
|
+
FROM important_dates AS d
|
|
1769
|
+
JOIN next_dates AS n
|
|
1770
|
+
ON n.id = d.id
|
|
1771
|
+
)
|
|
1772
|
+
SELECT
|
|
1773
|
+
*
|
|
1774
|
+
FROM computed
|
|
1775
|
+
WHERE
|
|
1776
|
+
days_until <= COALESCE(
|
|
1777
|
+
(
|
|
1778
|
+
SELECT
|
|
1779
|
+
CASE
|
|
1780
|
+
WHEN JSON_TYPE(value_json) = 'integer'
|
|
1781
|
+
THEN GREATEST(0, LEAST(2922, CAST(value_json AS BIGINT)))
|
|
1782
|
+
END
|
|
1783
|
+
FROM settings
|
|
1784
|
+
WHERE
|
|
1785
|
+
key = 'upcoming_days'
|
|
1786
|
+
),
|
|
1787
|
+
30
|
|
1788
|
+
);
|
|
1789
|
+
|
|
1790
|
+
/* q_upcoming_dates */
|
|
1791
|
+
/* q_upcoming_dates */
|
|
1792
|
+
CREATE VIEW q_upcoming_dates AS
|
|
1793
|
+
SELECT
|
|
1794
|
+
id,
|
|
1795
|
+
title,
|
|
1796
|
+
person_id,
|
|
1797
|
+
next_on,
|
|
1798
|
+
days_until,
|
|
1799
|
+
anniversary_no,
|
|
1800
|
+
remind_on,
|
|
1801
|
+
sensitivity
|
|
1802
|
+
FROM v_upcoming_dates
|
|
1803
|
+
WHERE
|
|
1804
|
+
person_id IS NULL
|
|
1805
|
+
OR EXISTS(
|
|
1806
|
+
SELECT
|
|
1807
|
+
1
|
|
1808
|
+
FROM _v_module_people AS p
|
|
1809
|
+
WHERE
|
|
1810
|
+
p.id = person_id
|
|
1811
|
+
);
|
|
1812
|
+
|
|
1813
|
+
/* v_her_wishes_open */
|
|
1814
|
+
/* v_her_wishes_open */
|
|
1815
|
+
CREATE VIEW v_her_wishes_open AS
|
|
1816
|
+
SELECT
|
|
1817
|
+
w.*,
|
|
1818
|
+
COALESCE(a.sensitivity, w.sensitivity) AS effective_sensitivity
|
|
1819
|
+
FROM wishes AS w
|
|
1820
|
+
JOIN _v_module_people AS her
|
|
1821
|
+
ON her.id = w.person_id AND her.module = 'her'
|
|
1822
|
+
LEFT JOIN _v_annotations AS a
|
|
1823
|
+
ON a.target_type = 'wishes' AND a.target_key = w.id
|
|
1824
|
+
WHERE
|
|
1825
|
+
w.deleted_at IS NULL
|
|
1826
|
+
AND w.status = 'confirmed'
|
|
1827
|
+
AND w.wish_state IN ('open', 'planned')
|
|
1828
|
+
AND COALESCE(a.hidden, 0) = 0;
|
|
1829
|
+
|
|
1830
|
+
/* q_wishes_open */
|
|
1831
|
+
/* q_wishes_open */
|
|
1832
|
+
CREATE VIEW q_wishes_open AS
|
|
1833
|
+
SELECT
|
|
1834
|
+
COUNT(*) AS "value"
|
|
1835
|
+
FROM v_her_wishes_open;
|
|
1836
|
+
|
|
1837
|
+
/* v_cycles */
|
|
1838
|
+
/* v_cycles */
|
|
1839
|
+
CREATE VIEW v_cycles AS
|
|
1840
|
+
WITH ordered AS (
|
|
1841
|
+
SELECT
|
|
1842
|
+
c.*,
|
|
1843
|
+
LAG(start_on) OVER (PARTITION BY person_id ORDER BY start_on NULLS FIRST) AS previous_start_on,
|
|
1844
|
+
LEAD(start_on) OVER (PARTITION BY person_id ORDER BY start_on NULLS FIRST) AS next_start_on
|
|
1845
|
+
FROM menstrual_cycles AS c
|
|
1846
|
+
LEFT JOIN _v_annotations AS a
|
|
1847
|
+
ON a.target_type = 'menstrual_cycles' AND a.target_key = c.id
|
|
1848
|
+
WHERE
|
|
1849
|
+
c.deleted_at IS NULL AND c.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
1850
|
+
), intervals AS (
|
|
1851
|
+
SELECT
|
|
1852
|
+
*,
|
|
1853
|
+
CAST(TRUNC(JULIANDAY(next_start_on) - JULIANDAY(start_on)) AS BIGINT) AS cycle_days,
|
|
1854
|
+
CAST(TRUNC(JULIANDAY(start_on) - JULIANDAY(previous_start_on)) AS BIGINT) AS previous_cycle_days,
|
|
1855
|
+
CASE
|
|
1856
|
+
WHEN NOT end_on IS NULL
|
|
1857
|
+
THEN CAST(TRUNC(JULIANDAY(end_on) - JULIANDAY(start_on)) AS BIGINT) + 1
|
|
1858
|
+
END AS period_days
|
|
1859
|
+
FROM ordered
|
|
1860
|
+
), estimated AS (
|
|
1861
|
+
SELECT
|
|
1862
|
+
*,
|
|
1863
|
+
COUNT(previous_cycle_days) OVER history AS history_intervals,
|
|
1864
|
+
CAST(TRUNC(ROUND(CAST(AVG(previous_cycle_days) OVER history AS DECIMAL))) AS BIGINT) AS estimated_cycle_days
|
|
1865
|
+
FROM intervals
|
|
1866
|
+
WINDOW history AS (
|
|
1867
|
+
PARTITION BY person_id
|
|
1868
|
+
ORDER BY start_on NULLS FIRST
|
|
1869
|
+
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW
|
|
1870
|
+
)
|
|
1871
|
+
)
|
|
1872
|
+
SELECT
|
|
1873
|
+
*,
|
|
1874
|
+
CASE
|
|
1875
|
+
WHEN history_intervals >= 2
|
|
1876
|
+
THEN DATE(start_on, PRINTF('+%d days', estimated_cycle_days))
|
|
1877
|
+
END AS predicted_next_on,
|
|
1878
|
+
'最近最多六个已记录间隔的均值;非医学结论,不能用于避孕' AS prediction_note
|
|
1879
|
+
FROM estimated;
|
|
1880
|
+
|
|
1881
|
+
/* v_her_active_hours */
|
|
1882
|
+
/* v_her_active_hours */
|
|
1883
|
+
CREATE VIEW v_her_active_hours AS
|
|
1884
|
+
SELECT
|
|
1885
|
+
CAST(SUBSTRING(m.sent_at FROM 12 FOR 2) AS BIGINT) AS hour,
|
|
1886
|
+
COUNT(*) AS message_count,
|
|
1887
|
+
COUNT(DISTINCT SUBSTRING(m.sent_at FROM 1 FOR 10)) AS active_days
|
|
1888
|
+
FROM v_messages AS m
|
|
1889
|
+
JOIN _v_module_people AS p
|
|
1890
|
+
ON p.id = m.person_id AND p.module = 'her'
|
|
1891
|
+
WHERE
|
|
1892
|
+
m.hidden = 0 AND NOT m.msg_type IN (80, 81)
|
|
1893
|
+
GROUP BY
|
|
1894
|
+
CAST(SUBSTRING(m.sent_at FROM 12 FOR 2) AS BIGINT);
|
|
1895
|
+
|
|
1896
|
+
/* v_utterances */
|
|
1897
|
+
/* v_utterances */
|
|
1898
|
+
CREATE VIEW v_utterances AS
|
|
1899
|
+
SELECT
|
|
1900
|
+
s.*,
|
|
1901
|
+
r.display_name AS recording_name,
|
|
1902
|
+
STRFTIME(
|
|
1903
|
+
'%Y-%m-%d %H:%M:%f',
|
|
1904
|
+
r.started_at,
|
|
1905
|
+
PRINTF('+%.3f seconds', s.start_ms / NULLIF(1000.0, 0))
|
|
1906
|
+
) AS spoken_at,
|
|
1907
|
+
CAST(NULL AS TEXT) AS person_id,
|
|
1908
|
+
CAST(NULL AS TEXT) AS person_name,
|
|
1909
|
+
CAST(NULL AS TEXT) AS party,
|
|
1910
|
+
CAST(NULL AS TEXT) AS speaker_confidence,
|
|
1911
|
+
GREATEST(COALESCE(a.hidden, 0), COALESCE(ra.hidden, 0)) AS hidden,
|
|
1912
|
+
CASE
|
|
1913
|
+
WHEN a.sensitivity = 'intimate' OR ra.sensitivity = 'intimate'
|
|
1914
|
+
THEN 'intimate'
|
|
1915
|
+
ELSE COALESCE(a.sensitivity, ra.sensitivity, 'normal')
|
|
1916
|
+
END AS sensitivity
|
|
1917
|
+
FROM audio_sentences AS s
|
|
1918
|
+
JOIN audio_records AS r
|
|
1919
|
+
ON r.sha1 = s.record_sha1
|
|
1920
|
+
LEFT JOIN _v_annotations AS a
|
|
1921
|
+
ON a.target_type = 'sentence'
|
|
1922
|
+
AND a.target_key = s.record_sha1 || ':' || s.sort_index
|
|
1923
|
+
LEFT JOIN _v_annotations AS ra
|
|
1924
|
+
ON ra.target_type = 'recording' AND ra.target_key = s.record_sha1;
|
|
1925
|
+
|
|
1926
|
+
/* v_her_mood_monthly */
|
|
1927
|
+
/* v_her_mood_monthly */
|
|
1928
|
+
CREATE VIEW v_her_mood_monthly AS
|
|
1929
|
+
WITH mood_labels AS (
|
|
1930
|
+
SELECT
|
|
1931
|
+
*,
|
|
1932
|
+
ROW_NUMBER() OVER (
|
|
1933
|
+
PARTITION BY target_type, target_key
|
|
1934
|
+
ORDER BY CASE WHEN basis = 'manual' OR updated_by = 'me' THEN 0 ELSE 1 END NULLS FIRST, updated_at DESC NULLS LAST, id DESC NULLS LAST
|
|
1935
|
+
) AS priority
|
|
1936
|
+
FROM labels
|
|
1937
|
+
WHERE
|
|
1938
|
+
kind = 'mood' AND deleted_at IS NULL
|
|
1939
|
+
), observations AS (
|
|
1940
|
+
SELECT
|
|
1941
|
+
'message' AS source_type,
|
|
1942
|
+
m.msg_id AS source_key,
|
|
1943
|
+
m.sent_at AS occurred_at,
|
|
1944
|
+
l."value" AS mood,
|
|
1945
|
+
m.sensitivity
|
|
1946
|
+
FROM v_messages AS m
|
|
1947
|
+
JOIN _v_module_people AS her
|
|
1948
|
+
ON her.id = m.person_id AND her.module = 'her'
|
|
1949
|
+
JOIN mood_labels AS l
|
|
1950
|
+
ON l.target_type = 'message' AND l.target_key = m.msg_id AND l.priority = 1
|
|
1951
|
+
WHERE
|
|
1952
|
+
m.hidden = 0 AND NOT m.msg_type IN (80, 81)
|
|
1953
|
+
UNION ALL
|
|
1954
|
+
SELECT
|
|
1955
|
+
'sentence',
|
|
1956
|
+
s.record_sha1 || ':' || s.sort_index,
|
|
1957
|
+
s.spoken_at,
|
|
1958
|
+
l."value",
|
|
1959
|
+
s.sensitivity
|
|
1960
|
+
FROM v_utterances AS s
|
|
1961
|
+
JOIN _v_module_people AS her
|
|
1962
|
+
ON her.id = s.person_id AND her.module = 'her'
|
|
1963
|
+
JOIN mood_labels AS l
|
|
1964
|
+
ON l.target_type = 'sentence'
|
|
1965
|
+
AND l.target_key = s.record_sha1 || ':' || s.sort_index
|
|
1966
|
+
AND l.priority = 1
|
|
1967
|
+
WHERE
|
|
1968
|
+
s.hidden = 0
|
|
1969
|
+
UNION ALL
|
|
1970
|
+
SELECT
|
|
1971
|
+
'journal',
|
|
1972
|
+
j.id,
|
|
1973
|
+
COALESCE(j.happened_on || ' 00:00:00', j.written_at),
|
|
1974
|
+
COALESCE(l."value", j.mood_polarity),
|
|
1975
|
+
COALESCE(a.sensitivity, j.sensitivity)
|
|
1976
|
+
FROM journal_entries AS j
|
|
1977
|
+
JOIN _v_module_people AS her
|
|
1978
|
+
ON her.id = j.about_person_id AND her.module = 'her'
|
|
1979
|
+
LEFT JOIN mood_labels AS l
|
|
1980
|
+
ON l.target_type = 'journal' AND l.target_key = j.id AND l.priority = 1
|
|
1981
|
+
LEFT JOIN _v_annotations AS a
|
|
1982
|
+
ON a.target_type = 'journal' AND a.target_key = j.id
|
|
1983
|
+
WHERE
|
|
1984
|
+
j.deleted_at IS NULL
|
|
1985
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
1986
|
+
AND NOT COALESCE(l."value", j.mood_polarity) IS NULL
|
|
1987
|
+
)
|
|
1988
|
+
SELECT
|
|
1989
|
+
SUBSTRING(occurred_at FROM 1 FOR 7) AS month,
|
|
1990
|
+
mood,
|
|
1991
|
+
COUNT(*) AS observation_count,
|
|
1992
|
+
SUM(CAST(source_type = 'message' AS INT)) AS message_count,
|
|
1993
|
+
SUM(CAST(source_type = 'sentence' AS INT)) AS sentence_count,
|
|
1994
|
+
SUM(CAST(source_type = 'journal' AS INT)) AS journal_count,
|
|
1995
|
+
CASE
|
|
1996
|
+
WHEN SUM(CAST(sensitivity = 'intimate' AS INT)) > 0
|
|
1997
|
+
THEN 'intimate'
|
|
1998
|
+
WHEN SUM(CAST(sensitivity = 'private' AS INT)) > 0
|
|
1999
|
+
THEN 'private'
|
|
2000
|
+
ELSE 'normal'
|
|
2001
|
+
END AS sensitivity
|
|
2002
|
+
FROM observations
|
|
2003
|
+
GROUP BY
|
|
2004
|
+
SUBSTRING(occurred_at FROM 1 FOR 7),
|
|
2005
|
+
mood;
|
|
2006
|
+
|
|
2007
|
+
/* v_her_preferences */
|
|
2008
|
+
/* v_her_preferences */
|
|
2009
|
+
CREATE VIEW v_her_preferences AS
|
|
2010
|
+
SELECT
|
|
2011
|
+
p.*,
|
|
2012
|
+
COALESCE(a.sensitivity, p.sensitivity) AS effective_sensitivity
|
|
2013
|
+
FROM preferences AS p
|
|
2014
|
+
JOIN _v_module_people AS her
|
|
2015
|
+
ON her.id = p.person_id AND her.module = 'her'
|
|
2016
|
+
LEFT JOIN _v_annotations AS a
|
|
2017
|
+
ON a.target_type = 'preferences' AND a.target_key = p.id
|
|
2018
|
+
WHERE
|
|
2019
|
+
p.deleted_at IS NULL AND p.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0;
|
|
2020
|
+
|
|
2021
|
+
/* v_persons_current */
|
|
2022
|
+
/* v_persons_current */
|
|
2023
|
+
CREATE VIEW v_persons_current AS
|
|
2024
|
+
WITH facts AS (
|
|
2025
|
+
SELECT
|
|
2026
|
+
f.*,
|
|
2027
|
+
ROW_NUMBER() OVER (
|
|
2028
|
+
PARTITION BY person_id, attribute
|
|
2029
|
+
ORDER BY COALESCE(valid_from, '') DESC NULLS LAST, f.updated_at DESC NULLS LAST, f.id DESC NULLS LAST
|
|
2030
|
+
) AS priority
|
|
2031
|
+
FROM profile_facts AS f
|
|
2032
|
+
LEFT JOIN _v_annotations AS a
|
|
2033
|
+
ON a.target_type = 'profile_facts' AND a.target_key = f.id
|
|
2034
|
+
WHERE
|
|
2035
|
+
f.deleted_at IS NULL
|
|
2036
|
+
AND f.status = 'confirmed'
|
|
2037
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
2038
|
+
AND (
|
|
2039
|
+
f.valid_from IS NULL OR f.valid_from <= VAULT_TODAY()
|
|
2040
|
+
)
|
|
2041
|
+
AND (
|
|
2042
|
+
f.valid_to IS NULL OR f.valid_to >= VAULT_TODAY()
|
|
2043
|
+
)
|
|
2044
|
+
), profiles AS (
|
|
2045
|
+
SELECT
|
|
2046
|
+
person_id,
|
|
2047
|
+
CAST(JSON_OBJECT_AGG(
|
|
2048
|
+
attribute,
|
|
2049
|
+
JSON_OBJECT(
|
|
2050
|
+
'value': "value",
|
|
2051
|
+
'value_num': value_num,
|
|
2052
|
+
'fact_id': id,
|
|
2053
|
+
'valid_from': valid_from,
|
|
2054
|
+
'valid_to': valid_to,
|
|
2055
|
+
'sensitivity': sensitivity
|
|
2056
|
+
)
|
|
2057
|
+
) AS TEXT) AS profile_json
|
|
2058
|
+
FROM facts
|
|
2059
|
+
WHERE
|
|
2060
|
+
priority = 1
|
|
2061
|
+
GROUP BY
|
|
2062
|
+
person_id
|
|
2063
|
+
)
|
|
2064
|
+
SELECT
|
|
2065
|
+
p.*,
|
|
2066
|
+
CAST(COALESCE(f.profile_json, '{}') AS TEXT) AS profile_json,
|
|
2067
|
+
CASE
|
|
2068
|
+
WHEN NOT VAULT_CALENDAR_DATE('solar', p.birth_on, NULL) IS NULL
|
|
2069
|
+
AND p.birth_on <= VAULT_TODAY()
|
|
2070
|
+
THEN CAST(SUBSTRING(VAULT_TODAY() FROM 1 FOR 4) AS BIGINT) - CAST(SUBSTRING(p.birth_on FROM 1 FOR 4) AS BIGINT) - CAST((
|
|
2071
|
+
SUBSTRING(VAULT_TODAY() FROM 6) < SUBSTRING(p.birth_on FROM 6)
|
|
2072
|
+
) AS INT)
|
|
2073
|
+
WHEN p.birth_on IS NULL
|
|
2074
|
+
AND p.birth_year <= CAST(SUBSTRING(VAULT_TODAY() FROM 1 FOR 4) AS BIGINT)
|
|
2075
|
+
THEN CAST(SUBSTRING(VAULT_TODAY() FROM 1 FOR 4) AS BIGINT) - p.birth_year
|
|
2076
|
+
END AS age,
|
|
2077
|
+
CASE WHEN p.birth_on IS NULL AND NOT p.birth_year IS NULL THEN 1 ELSE 0 END AS age_is_approx,
|
|
2078
|
+
CASE
|
|
2079
|
+
WHEN VAULT_CALENDAR_DATE('solar', p.birth_on, NULL) IS NULL
|
|
2080
|
+
THEN NULL
|
|
2081
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '01-20'
|
|
2082
|
+
THEN '摩羯座'
|
|
2083
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '02-19'
|
|
2084
|
+
THEN '水瓶座'
|
|
2085
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '03-21'
|
|
2086
|
+
THEN '双鱼座'
|
|
2087
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '04-20'
|
|
2088
|
+
THEN '白羊座'
|
|
2089
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '05-21'
|
|
2090
|
+
THEN '金牛座'
|
|
2091
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '06-22'
|
|
2092
|
+
THEN '双子座'
|
|
2093
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '07-23'
|
|
2094
|
+
THEN '巨蟹座'
|
|
2095
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '08-23'
|
|
2096
|
+
THEN '狮子座'
|
|
2097
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '09-23'
|
|
2098
|
+
THEN '处女座'
|
|
2099
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '10-24'
|
|
2100
|
+
THEN '天秤座'
|
|
2101
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '11-23'
|
|
2102
|
+
THEN '天蝎座'
|
|
2103
|
+
WHEN SUBSTRING(p.birth_on FROM 6) < '12-22'
|
|
2104
|
+
THEN '射手座'
|
|
2105
|
+
ELSE '摩羯座'
|
|
2106
|
+
END AS star_sign,
|
|
2107
|
+
VAULT_ZODIAC(p.birth_on) AS zodiac
|
|
2108
|
+
FROM persons AS p
|
|
2109
|
+
LEFT JOIN profiles AS f
|
|
2110
|
+
ON f.person_id = p.id
|
|
2111
|
+
WHERE
|
|
2112
|
+
p.deleted_at IS NULL;
|
|
2113
|
+
|
|
2114
|
+
/* v_her_profile */
|
|
2115
|
+
/* v_her_profile */
|
|
2116
|
+
CREATE VIEW v_her_profile AS
|
|
2117
|
+
SELECT
|
|
2118
|
+
p.*
|
|
2119
|
+
FROM v_persons_current AS p
|
|
2120
|
+
JOIN _v_module_people AS her
|
|
2121
|
+
ON her.id = p.id AND her.module = 'her'
|
|
2122
|
+
LEFT JOIN _v_annotations AS a
|
|
2123
|
+
ON a.target_type = 'persons' AND a.target_key = p.id
|
|
2124
|
+
WHERE
|
|
2125
|
+
COALESCE(a.hidden, 0) = 0;
|
|
2126
|
+
|
|
2127
|
+
/* v_her_reply_speed */
|
|
2128
|
+
/* v_her_reply_speed */
|
|
2129
|
+
CREATE VIEW v_her_reply_speed AS
|
|
2130
|
+
SELECT
|
|
2131
|
+
*
|
|
2132
|
+
FROM _v_us_reply_daily
|
|
2133
|
+
WHERE
|
|
2134
|
+
sender_module = 'her';
|
|
2135
|
+
|
|
2136
|
+
/* v_intimacy */
|
|
2137
|
+
/* v_intimacy */
|
|
2138
|
+
CREATE VIEW v_intimacy AS
|
|
2139
|
+
WITH numbered AS (
|
|
2140
|
+
SELECT
|
|
2141
|
+
e.*,
|
|
2142
|
+
ROW_NUMBER() OVER (
|
|
2143
|
+
PARTITION BY e.person_id, e.partner_id
|
|
2144
|
+
ORDER BY occurred_at NULLS FIRST, e.id NULLS FIRST
|
|
2145
|
+
) AS sequence_no,
|
|
2146
|
+
ROW_NUMBER() OVER (
|
|
2147
|
+
PARTITION BY e.person_id, e.partner_id, SUBSTRING(occurred_at FROM 1 FOR 10)
|
|
2148
|
+
ORDER BY occurred_at NULLS FIRST, e.id NULLS FIRST
|
|
2149
|
+
) AS day_sequence_no,
|
|
2150
|
+
LAG(SUBSTRING(occurred_at FROM 1 FOR 10)) OVER (
|
|
2151
|
+
PARTITION BY e.person_id, e.partner_id
|
|
2152
|
+
ORDER BY occurred_at NULLS FIRST, e.id NULLS FIRST
|
|
2153
|
+
) AS previous_on
|
|
2154
|
+
FROM intimacy_events AS e
|
|
2155
|
+
LEFT JOIN _v_annotations AS a
|
|
2156
|
+
ON a.target_type = 'intimacy_events' AND a.target_key = e.id
|
|
2157
|
+
WHERE
|
|
2158
|
+
e.deleted_at IS NULL AND e.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
2159
|
+
), cycles AS (
|
|
2160
|
+
SELECT
|
|
2161
|
+
e.*,
|
|
2162
|
+
c.id AS cycle_id,
|
|
2163
|
+
c.start_on AS cycle_start_on,
|
|
2164
|
+
COALESCE(c.next_start_on, c.predicted_next_on) AS reference_next_on,
|
|
2165
|
+
CASE
|
|
2166
|
+
WHEN NOT c.next_start_on IS NULL
|
|
2167
|
+
THEN 'recorded_next_period'
|
|
2168
|
+
WHEN NOT c.predicted_next_on IS NULL
|
|
2169
|
+
THEN 'historical_mean'
|
|
2170
|
+
ELSE 'insufficient_data'
|
|
2171
|
+
END AS window_basis
|
|
2172
|
+
FROM numbered AS e
|
|
2173
|
+
LEFT JOIN v_cycles AS c
|
|
2174
|
+
ON c.person_id = e.person_id
|
|
2175
|
+
AND c.start_on = (
|
|
2176
|
+
SELECT
|
|
2177
|
+
MAX(previous.start_on)
|
|
2178
|
+
FROM v_cycles AS previous
|
|
2179
|
+
WHERE
|
|
2180
|
+
previous.person_id = e.person_id
|
|
2181
|
+
AND previous.start_on <= SUBSTRING(e.occurred_at FROM 1 FOR 10)
|
|
2182
|
+
)
|
|
2183
|
+
)
|
|
2184
|
+
SELECT
|
|
2185
|
+
*,
|
|
2186
|
+
CAST(TRUNC(JULIANDAY(SUBSTRING(occurred_at FROM 1 FOR 10)) - JULIANDAY(previous_on)) AS BIGINT) AS gap_days,
|
|
2187
|
+
CAST(TRUNC(JULIANDAY(SUBSTRING(occurred_at FROM 1 FOR 10)) - JULIANDAY(cycle_start_on)) AS BIGINT) + 1 AS cycle_day,
|
|
2188
|
+
CASE
|
|
2189
|
+
WHEN DATE(reference_next_on, '-19 days') >= cycle_start_on
|
|
2190
|
+
THEN DATE(reference_next_on, '-19 days')
|
|
2191
|
+
END AS estimated_fertile_start_on,
|
|
2192
|
+
CASE
|
|
2193
|
+
WHEN DATE(reference_next_on, '-19 days') >= cycle_start_on
|
|
2194
|
+
THEN DATE(reference_next_on, '-14 days')
|
|
2195
|
+
END AS estimated_fertile_end_on,
|
|
2196
|
+
'日历估算假设排卵在下次来潮前14天,窗口为此前5天至估算日;非医学结论,不能用于避孕' AS window_note
|
|
2197
|
+
FROM cycles;
|
|
2198
|
+
|
|
2199
|
+
/* v_me_active_hours */
|
|
2200
|
+
/* v_me_active_hours */
|
|
2201
|
+
CREATE VIEW v_me_active_hours AS
|
|
2202
|
+
SELECT
|
|
2203
|
+
CAST(SUBSTRING(m.sent_at FROM 12 FOR 2) AS BIGINT) AS hour,
|
|
2204
|
+
COUNT(*) AS message_count,
|
|
2205
|
+
COUNT(DISTINCT SUBSTRING(m.sent_at FROM 1 FOR 10)) AS active_days
|
|
2206
|
+
FROM v_messages AS m
|
|
2207
|
+
JOIN _v_module_people AS p
|
|
2208
|
+
ON p.id = m.person_id AND p.module = 'me'
|
|
2209
|
+
WHERE
|
|
2210
|
+
m.hidden = 0 AND NOT m.msg_type IN (80, 81)
|
|
2211
|
+
GROUP BY
|
|
2212
|
+
CAST(SUBSTRING(m.sent_at FROM 12 FOR 2) AS BIGINT);
|
|
2213
|
+
|
|
2214
|
+
/* v_me_reply_speed */
|
|
2215
|
+
/* v_me_reply_speed */
|
|
2216
|
+
CREATE VIEW v_me_reply_speed AS
|
|
2217
|
+
SELECT
|
|
2218
|
+
*
|
|
2219
|
+
FROM _v_us_reply_daily
|
|
2220
|
+
WHERE
|
|
2221
|
+
sender_module = 'me';
|
|
2222
|
+
|
|
2223
|
+
/* v_timeline */
|
|
2224
|
+
/* v_timeline */
|
|
2225
|
+
CREATE VIEW v_timeline AS
|
|
2226
|
+
WITH visible_events AS (
|
|
2227
|
+
SELECT
|
|
2228
|
+
e.*,
|
|
2229
|
+
COALESCE(a.sensitivity, e.sensitivity) AS effective_sensitivity
|
|
2230
|
+
FROM events AS e
|
|
2231
|
+
LEFT JOIN _v_annotations AS a
|
|
2232
|
+
ON a.target_type = 'events' AND a.target_key = e.id
|
|
2233
|
+
WHERE
|
|
2234
|
+
e.deleted_at IS NULL AND e.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
2235
|
+
), rows AS (
|
|
2236
|
+
SELECT
|
|
2237
|
+
'events' AS source_type,
|
|
2238
|
+
e.id AS source_key,
|
|
2239
|
+
e.kind,
|
|
2240
|
+
e.start_at AS occurred_at,
|
|
2241
|
+
e.title,
|
|
2242
|
+
e.summary AS detail,
|
|
2243
|
+
e.effective_sensitivity AS sensitivity,
|
|
2244
|
+
e.id AS event_id
|
|
2245
|
+
FROM visible_events AS e
|
|
2246
|
+
UNION ALL
|
|
2247
|
+
SELECT
|
|
2248
|
+
'important_dates',
|
|
2249
|
+
d.id,
|
|
2250
|
+
d.kind,
|
|
2251
|
+
COALESCE(d.first_on, VAULT_CALENDAR_DATE(d.calendar, d.date_value, NULL)) || ' 00:00:00',
|
|
2252
|
+
d.title,
|
|
2253
|
+
d.note,
|
|
2254
|
+
COALESCE(a.sensitivity, d.sensitivity),
|
|
2255
|
+
NULL
|
|
2256
|
+
FROM important_dates AS d
|
|
2257
|
+
LEFT JOIN _v_annotations AS a
|
|
2258
|
+
ON a.target_type = 'important_dates' AND a.target_key = d.id
|
|
2259
|
+
WHERE
|
|
2260
|
+
d.deleted_at IS NULL AND d.status = 'confirmed' AND COALESCE(a.hidden, 0) = 0
|
|
2261
|
+
UNION ALL
|
|
2262
|
+
SELECT
|
|
2263
|
+
'gift_ideas',
|
|
2264
|
+
g.id,
|
|
2265
|
+
'gift',
|
|
2266
|
+
e.start_at,
|
|
2267
|
+
g.title,
|
|
2268
|
+
g.occasion,
|
|
2269
|
+
CASE
|
|
2270
|
+
WHEN COALESCE(a.sensitivity, g.sensitivity) = 'intimate'
|
|
2271
|
+
OR e.effective_sensitivity = 'intimate'
|
|
2272
|
+
THEN 'intimate'
|
|
2273
|
+
WHEN COALESCE(a.sensitivity, g.sensitivity) = 'private'
|
|
2274
|
+
OR e.effective_sensitivity = 'private'
|
|
2275
|
+
THEN 'private'
|
|
2276
|
+
ELSE 'normal'
|
|
2277
|
+
END,
|
|
2278
|
+
e.id
|
|
2279
|
+
FROM gift_ideas AS g
|
|
2280
|
+
JOIN visible_events AS e
|
|
2281
|
+
ON e.id = g.given_event_id
|
|
2282
|
+
LEFT JOIN _v_annotations AS a
|
|
2283
|
+
ON a.target_type = 'gift_ideas' AND a.target_key = g.id
|
|
2284
|
+
WHERE
|
|
2285
|
+
g.deleted_at IS NULL
|
|
2286
|
+
AND g.status = 'confirmed'
|
|
2287
|
+
AND g.idea_state = 'given'
|
|
2288
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
2289
|
+
AND e.kind <> 'gift'
|
|
2290
|
+
UNION ALL
|
|
2291
|
+
SELECT
|
|
2292
|
+
'wishes',
|
|
2293
|
+
w.id,
|
|
2294
|
+
'wish_done',
|
|
2295
|
+
e.start_at,
|
|
2296
|
+
w.text,
|
|
2297
|
+
w.category,
|
|
2298
|
+
CASE
|
|
2299
|
+
WHEN COALESCE(a.sensitivity, w.sensitivity) = 'intimate'
|
|
2300
|
+
OR e.effective_sensitivity = 'intimate'
|
|
2301
|
+
THEN 'intimate'
|
|
2302
|
+
WHEN COALESCE(a.sensitivity, w.sensitivity) = 'private'
|
|
2303
|
+
OR e.effective_sensitivity = 'private'
|
|
2304
|
+
THEN 'private'
|
|
2305
|
+
ELSE 'normal'
|
|
2306
|
+
END,
|
|
2307
|
+
e.id
|
|
2308
|
+
FROM wishes AS w
|
|
2309
|
+
JOIN visible_events AS e
|
|
2310
|
+
ON e.id = w.done_event_id
|
|
2311
|
+
LEFT JOIN _v_annotations AS a
|
|
2312
|
+
ON a.target_type = 'wishes' AND a.target_key = w.id
|
|
2313
|
+
WHERE
|
|
2314
|
+
w.deleted_at IS NULL
|
|
2315
|
+
AND w.status = 'confirmed'
|
|
2316
|
+
AND w.wish_state = 'done'
|
|
2317
|
+
AND COALESCE(a.hidden, 0) = 0
|
|
2318
|
+
)
|
|
2319
|
+
SELECT
|
|
2320
|
+
source_type,
|
|
2321
|
+
source_key,
|
|
2322
|
+
kind,
|
|
2323
|
+
occurred_at,
|
|
2324
|
+
title,
|
|
2325
|
+
detail,
|
|
2326
|
+
sensitivity,
|
|
2327
|
+
event_id
|
|
2328
|
+
FROM rows
|
|
2329
|
+
WHERE
|
|
2330
|
+
NOT occurred_at IS NULL;
|
|
2331
|
+
|
|
2332
|
+
/* v_us_spending_unassigned */
|
|
2333
|
+
/* v_us_spending_unassigned */
|
|
2334
|
+
CREATE VIEW v_us_spending_unassigned AS
|
|
2335
|
+
SELECT
|
|
2336
|
+
*,
|
|
2337
|
+
CASE
|
|
2338
|
+
WHEN needs_match_review = 1
|
|
2339
|
+
THEN 'pending_match'
|
|
2340
|
+
WHEN amount_cents IS NULL
|
|
2341
|
+
THEN 'unknown_amount'
|
|
2342
|
+
WHEN invalid_shares = 1
|
|
2343
|
+
THEN 'invalid_shares'
|
|
2344
|
+
ELSE 'unassigned_beneficiary'
|
|
2345
|
+
END AS reason
|
|
2346
|
+
FROM _v_spending_quality
|
|
2347
|
+
WHERE
|
|
2348
|
+
needs_match_review = 1
|
|
2349
|
+
OR amount_cents IS NULL
|
|
2350
|
+
OR invalid_shares = 1
|
|
2351
|
+
OR unassigned_cents > 0;
|
|
2352
|
+
|
|
2353
|
+
/* v_us_timeline */
|
|
2354
|
+
/* v_us_timeline */
|
|
2355
|
+
CREATE VIEW v_us_timeline AS
|
|
2356
|
+
SELECT
|
|
2357
|
+
t.*
|
|
2358
|
+
FROM v_timeline AS t
|
|
2359
|
+
WHERE
|
|
2360
|
+
(
|
|
2361
|
+
t.source_type = 'events'
|
|
2362
|
+
AND EXISTS(
|
|
2363
|
+
SELECT
|
|
2364
|
+
1
|
|
2365
|
+
FROM _v_us_events AS e
|
|
2366
|
+
WHERE
|
|
2367
|
+
e.id = t.event_id
|
|
2368
|
+
)
|
|
2369
|
+
)
|
|
2370
|
+
OR (
|
|
2371
|
+
t.source_type = 'important_dates'
|
|
2372
|
+
AND EXISTS(
|
|
2373
|
+
SELECT
|
|
2374
|
+
1
|
|
2375
|
+
FROM important_dates AS d
|
|
2376
|
+
WHERE
|
|
2377
|
+
d.id = t.source_key
|
|
2378
|
+
AND (
|
|
2379
|
+
d.person_id IS NULL
|
|
2380
|
+
OR EXISTS(
|
|
2381
|
+
SELECT
|
|
2382
|
+
1
|
|
2383
|
+
FROM _v_module_people AS p
|
|
2384
|
+
WHERE
|
|
2385
|
+
p.id = d.person_id
|
|
2386
|
+
)
|
|
2387
|
+
)
|
|
2388
|
+
)
|
|
2389
|
+
)
|
|
2390
|
+
OR (
|
|
2391
|
+
t.source_type IN ('gift_ideas', 'wishes')
|
|
2392
|
+
AND EXISTS(
|
|
2393
|
+
SELECT
|
|
2394
|
+
1
|
|
2395
|
+
FROM _v_us_events AS e
|
|
2396
|
+
WHERE
|
|
2397
|
+
e.id = t.event_id
|
|
2398
|
+
)
|
|
2399
|
+
AND (
|
|
2400
|
+
(
|
|
2401
|
+
t.source_type = 'gift_ideas'
|
|
2402
|
+
AND EXISTS(
|
|
2403
|
+
SELECT
|
|
2404
|
+
1
|
|
2405
|
+
FROM gift_ideas AS g
|
|
2406
|
+
JOIN _v_module_people AS p
|
|
2407
|
+
ON p.id = g.for_person_id
|
|
2408
|
+
WHERE
|
|
2409
|
+
g.id = t.source_key
|
|
2410
|
+
)
|
|
2411
|
+
)
|
|
2412
|
+
OR (
|
|
2413
|
+
t.source_type = 'wishes'
|
|
2414
|
+
AND EXISTS(
|
|
2415
|
+
SELECT
|
|
2416
|
+
1
|
|
2417
|
+
FROM wishes AS w
|
|
2418
|
+
JOIN _v_module_people AS p
|
|
2419
|
+
ON p.id = w.person_id
|
|
2420
|
+
WHERE
|
|
2421
|
+
w.id = t.source_key
|
|
2422
|
+
)
|
|
2423
|
+
)
|
|
2424
|
+
)
|
|
2425
|
+
);
|