@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,88 @@
|
|
|
1
|
+
-- 仅 PostgreSQL:整理出的事实统一记录“发生时间”与“时间精度”。
|
|
2
|
+
--
|
|
3
|
+
-- 业务背景:
|
|
4
|
+
-- 整理聊天、录音时,每条分析结果都应能回答“这是什么时候发生/说出的”。此前只有事件、承诺、健康、
|
|
5
|
+
-- 付出等表有时间列,喜好、档案事实、心愿、礼物想法、待办、承诺核对只有“记录时间”或到天的日期;
|
|
6
|
+
-- 整理规则(common.md)又要求填 time_precision,但库里没有这一列。本迁移补齐。
|
|
7
|
+
--
|
|
8
|
+
-- 两列的含义(都允许为空:材料里推不出来就留空,不编造;旧数据不回填):
|
|
9
|
+
-- * occurred_at:发生或被说出的时刻,上海时间文本 'YYYY-MM-DD HH:MM:SS'。事后讲述的事要推算到真正发生的时间,
|
|
10
|
+
-- 不是消息发送时间,更不是入库时间;
|
|
11
|
+
-- * time_precision:occurred_at(或该表既有的时间列)有多可信——
|
|
12
|
+
-- exact 材料里明说了时刻;或记录的是“某句话说了什么”,时刻就是那条消息/录音的时间;
|
|
13
|
+
-- approximate 记录的是“某件事发生了”,时刻只是从消息时间戳推出来的;
|
|
14
|
+
-- date 只知道是哪一天(时刻部分不可信,可填当天 00:00:00 或留空 occurred_at);
|
|
15
|
+
-- month 只知道是哪个月(填当月 1 日)。
|
|
16
|
+
--
|
|
17
|
+
-- 范围:
|
|
18
|
+
-- 1. 已有完整时间列的表只补 time_precision:events(start_at)、promises(promised_at)、
|
|
19
|
+
-- health_events(occurred_at)、giving_ledger(occurred_at);
|
|
20
|
+
-- 2. 缺少时刻的表补 occurred_at + time_precision:preferences、profile_facts、wishes、gift_ideas、todos、promise_checks;
|
|
21
|
+
-- 3. 不动:important_dates(日期本身就是数据)、daily_presence(按天)、self_hypotheses(period_from/to)、
|
|
22
|
+
-- relations(since_on/until_on)、intimacy_events(已有 time_known 表达“只知道日期”),
|
|
23
|
+
-- 以及订单、支付、银行流水等导入时自带时间的表。
|
|
24
|
+
--
|
|
25
|
+
-- 与既有列的关系:preferences.first_noted_on/last_noted_on 是“第一次/最后一次被提到的日期”,
|
|
26
|
+
-- profile_facts.valid_from/valid_to 是“属性生效区间”,wishes.said_on 是说出的日期,这些语义不变;
|
|
27
|
+
-- occurred_at 记的是这条记录对应的那一次发生/表述的时刻。
|
|
28
|
+
--
|
|
29
|
+
-- 兼容:只新增可空列,不改写已有数据,不回填历史行(历史行需要补时再另行授权)。
|
|
30
|
+
SELECT pg_advisory_xact_lock(hashtext('relation-vault:write'));
|
|
31
|
+
|
|
32
|
+
-- 1) 只补 time_precision:这些表已有自己的时间列,meaning 说明精度针对哪一列。
|
|
33
|
+
DO $$
|
|
34
|
+
DECLARE
|
|
35
|
+
item record;
|
|
36
|
+
BEGIN
|
|
37
|
+
FOR item IN
|
|
38
|
+
SELECT * FROM (VALUES
|
|
39
|
+
('events', 'start_at'),
|
|
40
|
+
('promises', 'promised_at'),
|
|
41
|
+
('health_events', 'occurred_at'),
|
|
42
|
+
('giving_ledger', 'occurred_at')
|
|
43
|
+
) AS t(table_name, time_column)
|
|
44
|
+
LOOP
|
|
45
|
+
EXECUTE format(
|
|
46
|
+
'ALTER TABLE public.%I ADD COLUMN IF NOT EXISTS time_precision text
|
|
47
|
+
CHECK (time_precision IS NULL OR time_precision IN (''exact'', ''approximate'', ''date'', ''month''))',
|
|
48
|
+
item.table_name);
|
|
49
|
+
EXECUTE format(
|
|
50
|
+
'COMMENT ON COLUMN public.%I.time_precision IS %L',
|
|
51
|
+
item.table_name,
|
|
52
|
+
format('时间精度(可空):%s 有多可信。exact=材料里明说了时刻,或记录的是“说了什么”而时刻就是消息时间;approximate=记录的是“发生了什么”,时刻只是从消息时间推出来的;date=只知道哪一天;month=只知道哪个月;NULL=未标注', item.time_column));
|
|
53
|
+
END LOOP;
|
|
54
|
+
END $$;
|
|
55
|
+
|
|
56
|
+
-- 2) 补 occurred_at + time_precision。
|
|
57
|
+
DO $$
|
|
58
|
+
DECLARE
|
|
59
|
+
item record;
|
|
60
|
+
BEGIN
|
|
61
|
+
FOR item IN
|
|
62
|
+
SELECT * FROM (VALUES
|
|
63
|
+
('preferences', '这条喜好/雷区被说出或观察到的时刻(first_noted_on/last_noted_on 只记被提到的日期)'),
|
|
64
|
+
('profile_facts', '这条属性被说出或确认的时刻(valid_from/valid_to 是属性的生效区间,不等于说出的时刻)'),
|
|
65
|
+
('wishes', '这个心愿被说出的时刻(said_on 只记日期)'),
|
|
66
|
+
('gift_ideas', '这个礼物想法被提到的时刻'),
|
|
67
|
+
('todos', '这条待办被提出的时刻(due_on 是截止日,done_at 是完成时刻)'),
|
|
68
|
+
('promise_checks', '兑现情况被观察到的时刻(checked_on 只记核对日期)')
|
|
69
|
+
) AS t(table_name, meaning)
|
|
70
|
+
LOOP
|
|
71
|
+
EXECUTE format(
|
|
72
|
+
'ALTER TABLE public.%I ADD COLUMN IF NOT EXISTS occurred_at text
|
|
73
|
+
CHECK (occurred_at IS NULL OR occurred_at ~ ''^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'')',
|
|
74
|
+
item.table_name);
|
|
75
|
+
EXECUTE format(
|
|
76
|
+
'ALTER TABLE public.%I ADD COLUMN IF NOT EXISTS time_precision text
|
|
77
|
+
CHECK (time_precision IS NULL OR time_precision IN (''exact'', ''approximate'', ''date'', ''month''))',
|
|
78
|
+
item.table_name);
|
|
79
|
+
EXECUTE format(
|
|
80
|
+
'COMMENT ON COLUMN public.%I.occurred_at IS %L',
|
|
81
|
+
item.table_name,
|
|
82
|
+
format('发生/说出时间(上海时间 YYYY-MM-DD HH:MM:SS,可空):%s;事后讲述的要推算到真正发生的时刻,推算不出来留空,不是入库时间', item.meaning));
|
|
83
|
+
EXECUTE format(
|
|
84
|
+
'COMMENT ON COLUMN public.%I.time_precision IS %L',
|
|
85
|
+
item.table_name,
|
|
86
|
+
'时间精度(可空):occurred_at 有多可信。exact=材料里明说了时刻,或记录的是“说了什么”而时刻就是消息时间;approximate=记录的是“发生了什么”,时刻只是从消息时间推出来的;date=只知道哪一天;month=只知道哪个月;NULL=未标注');
|
|
87
|
+
END LOOP;
|
|
88
|
+
END $$;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
-- 仅 PostgreSQL:把 Agent 写下的文字从日记表 journal_entries 拆到新表 agent_notes。
|
|
2
|
+
--
|
|
3
|
+
-- 业务背景(2026-11-10 用户要求):
|
|
4
|
+
-- 日记是用户自己写的原始数据,只应有两类来源:网页手写(origin=h5)与旧仓库导入(legacy_md)。
|
|
5
|
+
-- 此前 Skill 的 remember 把每次附带的 text 存成一篇日记(origin=skill),Agent 也直接往日记表写过分析摘录,
|
|
6
|
+
-- 结果日记页、"今天"信息流、日历和检索里混进了"回复你的两个问题""修正某某笔误""9 月账单平台归属 1-20"这类
|
|
7
|
+
-- 给 Agent 的指令、Agent 的改动说明和分析过程。整理出来的结论应当是结构化行与 Wiki,这些文字两者都不是。
|
|
8
|
+
--
|
|
9
|
+
-- 拆分口径:
|
|
10
|
+
-- 1. 判定"Agent 写的":origin='skill',或 created_by 以 'skill:' / 'agent:' 开头(旧 Agent 协议的写入者)。
|
|
11
|
+
-- 用户手写与导入的日记 created_by 不会是这两种前缀;软删除的行一并搬走,保留原 deleted_at。
|
|
12
|
+
-- 2. 搬迁保留原 id、正文、时间与操作者,origin 改记为 remember(remember 的 text)或 agent(Agent 直接写入)。
|
|
13
|
+
-- batch_id 取原日记插入时的审计批次(存在于 vault_batches 才填),用于把说明和那次写入对上。
|
|
14
|
+
-- 3. 证据链:指向这些行的 evidence_links(source_type='journal') 改为 source_type='user_stated',source_key 不变(即 agent_notes.id)。
|
|
15
|
+
-- 'user_stated' 是 evidence_links 原有的取值("用户亲口说的"),无需改约束。
|
|
16
|
+
-- 4. 检索索引 search_docs 中这些行的条目删除:Agent 记录不进日记,也不进全文检索;需要时用 SQL 直接查 agent_notes。
|
|
17
|
+
-- 5. vault_batches.journal_id 改名 note_id,值不变(同一 id 现在指向 agent_notes)。
|
|
18
|
+
-- 6. 日记表 origin 取值去掉 skill(搬迁后不再有此类行,Skill 也不再写日记);cli 保留,代表用户经命令行写的日记。
|
|
19
|
+
-- 只搬迁 Agent 写的行;用户日记的内容、id 与证据不变。搬迁与删除都经审计触发器留痕。
|
|
20
|
+
SELECT pg_advisory_xact_lock(hashtext('relation-vault:write'));
|
|
21
|
+
|
|
22
|
+
-- 1. 新表:Agent 记录。标准列与其他 Skill 表一致,经 vault_register_skill_table 挂写会话保护、审计与授权,
|
|
23
|
+
-- remember 批次撤回时与同批其他写入一起还原。
|
|
24
|
+
CREATE TABLE public.agent_notes (
|
|
25
|
+
id text PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
26
|
+
batch_id text REFERENCES public.vault_batches(id),
|
|
27
|
+
origin text NOT NULL DEFAULT 'remember' CHECK (origin IN ('remember', 'agent')),
|
|
28
|
+
about_person_id text REFERENCES public.persons(id),
|
|
29
|
+
written_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
30
|
+
happened_on text CHECK (happened_on IS NULL OR happened_on ~ '^\d{4}-\d{2}-\d{2}$'),
|
|
31
|
+
title text NOT NULL,
|
|
32
|
+
content text NOT NULL,
|
|
33
|
+
sensitivity text NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'private', 'intimate')),
|
|
34
|
+
provenance text CHECK (provenance IS NULL OR provenance IN ('user', 'inferred', 'import')),
|
|
35
|
+
created_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
36
|
+
created_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
37
|
+
updated_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
38
|
+
updated_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
39
|
+
deleted_at text
|
|
40
|
+
);
|
|
41
|
+
COMMENT ON TABLE public.agent_notes IS 'Agent 记录:remember 附带的文字(用户对 Agent 说的原话或本次改动说明)与 Agent 写下的过程说明;不是日记,不在日记页、信息流和检索中出现';
|
|
42
|
+
COMMENT ON COLUMN public.agent_notes.id IS '行 ID(随机 UUID 文本);evidence_links(source_type=user_stated) 的 source_key 指向它';
|
|
43
|
+
COMMENT ON COLUMN public.agent_notes.batch_id IS '写入它的 Skill 批次(vault_batches.id),可空';
|
|
44
|
+
COMMENT ON COLUMN public.agent_notes.origin IS 'remember=remember 的 text 自动保存,agent=Agent 直接写下的说明(旧版写在日记表里的分析摘录)';
|
|
45
|
+
COMMENT ON COLUMN public.agent_notes.about_person_id IS '主要涉及的人物,可空';
|
|
46
|
+
COMMENT ON COLUMN public.agent_notes.written_at IS '写下时间(上海时间)';
|
|
47
|
+
COMMENT ON COLUMN public.agent_notes.happened_on IS '所说之事的发生日期 YYYY-MM-DD,可空';
|
|
48
|
+
COMMENT ON COLUMN public.agent_notes.title IS '标题(默认取正文首行)';
|
|
49
|
+
COMMENT ON COLUMN public.agent_notes.content IS '正文原文';
|
|
50
|
+
COMMENT ON COLUMN public.agent_notes.sensitivity IS '敏感级别:normal/private/intimate';
|
|
51
|
+
COMMENT ON COLUMN public.agent_notes.provenance IS '来源标记:user=用户亲口说的,inferred=Agent 推断,import=导入程序写入';
|
|
52
|
+
COMMENT ON COLUMN public.agent_notes.created_at IS '创建时间(上海时间)';
|
|
53
|
+
COMMENT ON COLUMN public.agent_notes.created_by IS '创建者';
|
|
54
|
+
COMMENT ON COLUMN public.agent_notes.updated_at IS '更新时间(上海时间)';
|
|
55
|
+
COMMENT ON COLUMN public.agent_notes.updated_by IS '更新者';
|
|
56
|
+
COMMENT ON COLUMN public.agent_notes.deleted_at IS '软删除时间';
|
|
57
|
+
CREATE INDEX ix_agent_notes_batch ON public.agent_notes(batch_id);
|
|
58
|
+
CREATE INDEX ix_agent_notes_written ON public.agent_notes(written_at);
|
|
59
|
+
SELECT public.vault_register_skill_table('agent_notes');
|
|
60
|
+
|
|
61
|
+
-- 2. 搬迁 Agent 写的日记行。审计表只扫一次取各行的插入批次。
|
|
62
|
+
CREATE TEMP TABLE moved_journal_notes ON COMMIT DROP AS
|
|
63
|
+
SELECT j.id
|
|
64
|
+
FROM public.journal_entries j
|
|
65
|
+
WHERE j.origin = 'skill' OR j.created_by LIKE 'skill:%' OR j.created_by LIKE 'agent:%';
|
|
66
|
+
|
|
67
|
+
INSERT INTO public.agent_notes (
|
|
68
|
+
id, batch_id, origin, about_person_id, written_at, happened_on, title, content,
|
|
69
|
+
sensitivity, provenance, created_at, created_by, updated_at, updated_by, deleted_at
|
|
70
|
+
)
|
|
71
|
+
SELECT j.id,
|
|
72
|
+
b.id,
|
|
73
|
+
CASE WHEN j.origin = 'skill' THEN 'remember' ELSE 'agent' END,
|
|
74
|
+
j.about_person_id, j.written_at, j.happened_on, j.title, j.content,
|
|
75
|
+
j.sensitivity, j.provenance, j.created_at, j.created_by, j.updated_at, j.updated_by, j.deleted_at
|
|
76
|
+
FROM public.journal_entries j
|
|
77
|
+
JOIN moved_journal_notes m ON m.id = j.id
|
|
78
|
+
LEFT JOIN (
|
|
79
|
+
SELECT DISTINCT ON (a.primary_key_json::jsonb ->> 'id')
|
|
80
|
+
a.primary_key_json::jsonb ->> 'id' AS journal_id, a.change_set
|
|
81
|
+
FROM public.audit_log a
|
|
82
|
+
WHERE a.table_name = 'journal_entries' AND a.op = 'insert' AND a.change_set IS NOT NULL
|
|
83
|
+
ORDER BY a.primary_key_json::jsonb ->> 'id', a.id
|
|
84
|
+
) ins ON ins.journal_id = j.id
|
|
85
|
+
LEFT JOIN public.vault_batches b ON b.id = ins.change_set;
|
|
86
|
+
|
|
87
|
+
-- 3. 证据改指 agent_notes;检索索引移除;日记行删除(审计保留删除前全文)。
|
|
88
|
+
UPDATE public.evidence_links e
|
|
89
|
+
SET source_type = 'user_stated'
|
|
90
|
+
FROM moved_journal_notes m
|
|
91
|
+
WHERE e.source_type = 'journal' AND e.source_key = m.id;
|
|
92
|
+
|
|
93
|
+
DELETE FROM public.search_docs d
|
|
94
|
+
USING moved_journal_notes m
|
|
95
|
+
WHERE d.source_type = 'journal' AND d.source_key = m.id;
|
|
96
|
+
|
|
97
|
+
DELETE FROM public.journal_entries j
|
|
98
|
+
USING moved_journal_notes m
|
|
99
|
+
WHERE j.id = m.id;
|
|
100
|
+
|
|
101
|
+
-- 4. 批次表原话列改名。
|
|
102
|
+
ALTER TABLE public.vault_batches RENAME COLUMN journal_id TO note_id;
|
|
103
|
+
COMMENT ON COLUMN public.vault_batches.note_id IS '本批 remember 保存的 Agent 记录 ID(agent_notes.id)';
|
|
104
|
+
|
|
105
|
+
-- 5. 日记 origin 去掉 skill;表注释改为当前口径。
|
|
106
|
+
DO $$ DECLARE con text; BEGIN
|
|
107
|
+
SELECT k.conname INTO con
|
|
108
|
+
FROM pg_constraint k
|
|
109
|
+
WHERE k.conrelid = 'public.journal_entries'::regclass AND k.contype = 'c'
|
|
110
|
+
AND pg_get_constraintdef(k.oid) LIKE '%origin%legacy_md%';
|
|
111
|
+
IF con IS NOT NULL THEN
|
|
112
|
+
EXECUTE format('ALTER TABLE public.journal_entries DROP CONSTRAINT %I', con);
|
|
113
|
+
END IF;
|
|
114
|
+
ALTER TABLE public.journal_entries ADD CONSTRAINT journal_entries_origin_check
|
|
115
|
+
CHECK (origin IN ('h5', 'cli', 'legacy_md', 'ai_dictation'));
|
|
116
|
+
END $$;
|
|
117
|
+
COMMENT ON TABLE public.journal_entries IS '日记:用户自己写的原始数据(网页手写 h5、命令行 cli、旧仓库导入 legacy_md);Agent 写的文字在 agent_notes,不写这里';
|
|
118
|
+
COMMENT ON COLUMN public.journal_entries.origin IS 'h5=网页手写,cli=命令行写入,legacy_md=旧 Markdown 仓库导入,ai_dictation=语音口述';
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
-- 仅 PostgreSQL:人生履历(工作、学历、居住)三张表,以及两项口径修正。
|
|
2
|
+
--
|
|
3
|
+
-- 业务背景(2026-11-10 用户访谈,方案 tmp/人生OS扩表方案.md §3.1):
|
|
4
|
+
-- 项目定位为“人生 OS”,工作经历、学历和居住史是人生时间轴的骨架,收入、资产、证件等后续领域都会挂在上面。
|
|
5
|
+
-- 原先只有 Skill 运行时建的 housing_leases(仅租房),自有房、老家、宿舍放不进去;工作与学历只能散落在 profile_facts。
|
|
6
|
+
--
|
|
7
|
+
-- 共同口径:
|
|
8
|
+
-- * 标准列与 0155 的知识表一致(status/confidence/sensitivity/note/provenance/created_*/updated_*/deleted_at),
|
|
9
|
+
-- 经 vault_register_skill_table 登记:写会话保护、逐行审计、整理批次可撤回;证据挂 evidence_links。
|
|
10
|
+
-- * 每行都有 person_id(外键 persons),默认写本人;家人的履历同样可记。
|
|
11
|
+
-- * 履历日期允许只到年或月(YYYY / YYYY-MM / YYYY-MM-DD):人对多年前的入职、入学、搬家通常只记得到月份,
|
|
12
|
+
-- 强制精确到日只会逼写入方编造日期。文本按字典序比较即可排序;起止先后的 CHECK 把较长的一方截到较短的长度再比,
|
|
13
|
+
-- 这样“2020-05 入职、2020 离职”这种精度不同的写法不会被误拒。
|
|
14
|
+
-- * 租约起止(lease_start_on/lease_end_on)是合同上的确切日期,沿用 housing_leases 的 YYYY-MM-DD。
|
|
15
|
+
--
|
|
16
|
+
-- 数据变动:
|
|
17
|
+
-- 1. housing_leases 若存在(只在用户正式库里由 Skill 运行时建过;全新库、演示库、测试库没有):
|
|
18
|
+
-- 按原 id 复制进 residences,指向它的证据改指 residences,原表删除(不另存副本),
|
|
19
|
+
-- 原名改为只读兼容视图。已知影响:写过 housing_leases 的旧整理批次之后不能再整体撤回(撤回目标已是视图),用户已确认接受。
|
|
20
|
+
-- 2. intimacy_events 加 time_precision 并按 time_known 回填(见第 6 节)。
|
|
21
|
+
-- 3. v_typed_orders_explorer 补上停车订单(见第 7 节)。
|
|
22
|
+
-- 不创建备份。
|
|
23
|
+
SELECT pg_advisory_xact_lock(hashtext('relation-vault:write'));
|
|
24
|
+
|
|
25
|
+
-- 履历日期:年、年月或年月日。
|
|
26
|
+
CREATE OR REPLACE FUNCTION public.vault_is_partial_date(v text) RETURNS boolean
|
|
27
|
+
LANGUAGE sql IMMUTABLE AS $$ SELECT v ~ '^\d{4}(-\d{2}(-\d{2})?)?$' $$;
|
|
28
|
+
COMMENT ON FUNCTION public.vault_is_partial_date(text) IS '履历类日期校验:允许 YYYY、YYYY-MM、YYYY-MM-DD 三种精度';
|
|
29
|
+
|
|
30
|
+
-- 起止先后:较长的一方截到较短一方的长度再比较,精度不同时只比共同部分(“2020-05”起、“2020”止视为合法)。
|
|
31
|
+
CREATE OR REPLACE FUNCTION public.vault_partial_date_le(a text, b text) RETURNS boolean
|
|
32
|
+
LANGUAGE sql IMMUTABLE AS $$
|
|
33
|
+
SELECT a IS NULL OR b IS NULL
|
|
34
|
+
OR left(a, least(length(a), length(b))) <= left(b, least(length(a), length(b)))
|
|
35
|
+
$$;
|
|
36
|
+
COMMENT ON FUNCTION public.vault_partial_date_le(text, text) IS '履历类日期 a 不晚于 b(只比较两者共同精度的部分;任一为空视为成立)';
|
|
37
|
+
|
|
38
|
+
-- 1. 工作经历:一段任职一行。换岗、升职若公司不变,按“一段任职”记一行,职位变化写 note 或另起一行均可,由写入方按事实拆。
|
|
39
|
+
CREATE TABLE public.career_history (
|
|
40
|
+
id text PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
41
|
+
status text NOT NULL DEFAULT 'confirmed' CHECK (status IN ('proposed', 'confirmed', 'disputed', 'retired')),
|
|
42
|
+
confidence text NOT NULL DEFAULT 'high' CHECK (confidence IN ('high', 'medium', 'low')),
|
|
43
|
+
sensitivity text NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'private', 'intimate')),
|
|
44
|
+
note text,
|
|
45
|
+
provenance text CHECK (provenance IS NULL OR provenance IN ('user', 'inferred', 'import')),
|
|
46
|
+
created_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
47
|
+
created_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
48
|
+
updated_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
49
|
+
updated_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
50
|
+
deleted_at text,
|
|
51
|
+
person_id text NOT NULL REFERENCES public.persons(id),
|
|
52
|
+
company text NOT NULL CHECK (btrim(company) <> ''),
|
|
53
|
+
department text,
|
|
54
|
+
title text,
|
|
55
|
+
grade text,
|
|
56
|
+
employment_type text NOT NULL DEFAULT 'full_time'
|
|
57
|
+
CHECK (employment_type IN ('full_time', 'intern', 'part_time', 'freelance', 'other')),
|
|
58
|
+
city text,
|
|
59
|
+
start_on text NOT NULL CHECK (public.vault_is_partial_date(start_on)),
|
|
60
|
+
end_on text CHECK (end_on IS NULL OR public.vault_is_partial_date(end_on)),
|
|
61
|
+
leave_reason text,
|
|
62
|
+
offer_monthly_cents bigint CHECK (offer_monthly_cents IS NULL OR offer_monthly_cents >= 0),
|
|
63
|
+
offer_months numeric(4, 1) CHECK (offer_months IS NULL OR offer_months > 0),
|
|
64
|
+
CHECK (public.vault_partial_date_le(start_on, end_on))
|
|
65
|
+
);
|
|
66
|
+
COMMENT ON TABLE public.career_history IS '工作经历:一段任职一行(公司、岗位、起止、offer 薪资);实际到手收入另记,不在本表';
|
|
67
|
+
COMMENT ON COLUMN public.career_history.person_id IS '任职的人,默认本人';
|
|
68
|
+
COMMENT ON COLUMN public.career_history.company IS '公司或单位名称(原样记录,不缩写)';
|
|
69
|
+
COMMENT ON COLUMN public.career_history.department IS '部门,可空';
|
|
70
|
+
COMMENT ON COLUMN public.career_history.title IS '岗位或职位名称,可空';
|
|
71
|
+
COMMENT ON COLUMN public.career_history.grade IS '职级(如 P6、T3-2),可空';
|
|
72
|
+
COMMENT ON COLUMN public.career_history.employment_type IS 'full_time=全职,intern=实习,part_time=兼职,freelance=自由职业,other=其他';
|
|
73
|
+
COMMENT ON COLUMN public.career_history.city IS '工作城市,可空';
|
|
74
|
+
COMMENT ON COLUMN public.career_history.start_on IS '入职日期:YYYY、YYYY-MM 或 YYYY-MM-DD,只记得到月就写到月,不要补日';
|
|
75
|
+
COMMENT ON COLUMN public.career_history.end_on IS '离职日期,精度同 start_on;仍在职为空';
|
|
76
|
+
COMMENT ON COLUMN public.career_history.leave_reason IS '离职原因,可空';
|
|
77
|
+
COMMENT ON COLUMN public.career_history.offer_monthly_cents IS 'offer 口径的税前月薪(分);实际到手以收入记录为准';
|
|
78
|
+
COMMENT ON COLUMN public.career_history.offer_months IS 'offer 口径每年发几个月(如 13、14.5)';
|
|
79
|
+
CREATE INDEX ix_career_history_person ON public.career_history(person_id, start_on);
|
|
80
|
+
|
|
81
|
+
-- 2. 学历:一段求学一行。
|
|
82
|
+
CREATE TABLE public.education_history (
|
|
83
|
+
id text PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
84
|
+
status text NOT NULL DEFAULT 'confirmed' CHECK (status IN ('proposed', 'confirmed', 'disputed', 'retired')),
|
|
85
|
+
confidence text NOT NULL DEFAULT 'high' CHECK (confidence IN ('high', 'medium', 'low')),
|
|
86
|
+
sensitivity text NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'private', 'intimate')),
|
|
87
|
+
note text,
|
|
88
|
+
provenance text CHECK (provenance IS NULL OR provenance IN ('user', 'inferred', 'import')),
|
|
89
|
+
created_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
90
|
+
created_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
91
|
+
updated_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
92
|
+
updated_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
93
|
+
deleted_at text,
|
|
94
|
+
person_id text NOT NULL REFERENCES public.persons(id),
|
|
95
|
+
school text NOT NULL CHECK (btrim(school) <> ''),
|
|
96
|
+
level text NOT NULL CHECK (level IN (
|
|
97
|
+
'primary', 'junior_high', 'senior_high', 'vocational', 'associate', 'bachelor', 'master', 'doctor', 'other'
|
|
98
|
+
)),
|
|
99
|
+
major text,
|
|
100
|
+
start_on text CHECK (start_on IS NULL OR public.vault_is_partial_date(start_on)),
|
|
101
|
+
end_on text CHECK (end_on IS NULL OR public.vault_is_partial_date(end_on)),
|
|
102
|
+
graduation_state text NOT NULL DEFAULT 'unknown'
|
|
103
|
+
CHECK (graduation_state IN ('graduated', 'studying', 'dropped', 'unknown')),
|
|
104
|
+
CHECK (public.vault_partial_date_le(start_on, end_on))
|
|
105
|
+
);
|
|
106
|
+
COMMENT ON TABLE public.education_history IS '学历:一段求学一行(学校、学历层次、专业、起止、是否毕业)';
|
|
107
|
+
COMMENT ON COLUMN public.education_history.person_id IS '求学的人,默认本人';
|
|
108
|
+
COMMENT ON COLUMN public.education_history.school IS '学校名称';
|
|
109
|
+
COMMENT ON COLUMN public.education_history.level IS 'primary=小学,junior_high=初中,senior_high=高中,vocational=中专/职高,associate=大专,bachelor=本科,master=硕士,doctor=博士,other=其他';
|
|
110
|
+
COMMENT ON COLUMN public.education_history.major IS '专业,可空';
|
|
111
|
+
COMMENT ON COLUMN public.education_history.start_on IS '入学:YYYY、YYYY-MM 或 YYYY-MM-DD,可空';
|
|
112
|
+
COMMENT ON COLUMN public.education_history.end_on IS '毕业或离校,精度同 start_on;在读为空';
|
|
113
|
+
COMMENT ON COLUMN public.education_history.graduation_state IS 'graduated=已毕业,studying=在读,dropped=未完成,unknown=不清楚';
|
|
114
|
+
CREATE INDEX ix_education_history_person ON public.education_history(person_id, start_on);
|
|
115
|
+
|
|
116
|
+
-- 3. 居住史:住过的每一处一行,租房、自有房、老家、宿舍都算。
|
|
117
|
+
-- 同一处房子续租不另起一行(改 lease_end_on);换房另起一行。
|
|
118
|
+
CREATE TABLE public.residences (
|
|
119
|
+
id text PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
120
|
+
status text NOT NULL DEFAULT 'confirmed' CHECK (status IN ('proposed', 'confirmed', 'disputed', 'retired')),
|
|
121
|
+
confidence text NOT NULL DEFAULT 'high' CHECK (confidence IN ('high', 'medium', 'low')),
|
|
122
|
+
sensitivity text NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('normal', 'private', 'intimate')),
|
|
123
|
+
note text,
|
|
124
|
+
provenance text CHECK (provenance IS NULL OR provenance IN ('user', 'inferred', 'import')),
|
|
125
|
+
created_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
126
|
+
created_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
127
|
+
updated_at text NOT NULL DEFAULT public.vault_now_text(),
|
|
128
|
+
updated_by text NOT NULL DEFAULT public.vault_current_actor(),
|
|
129
|
+
deleted_at text,
|
|
130
|
+
person_id text NOT NULL REFERENCES public.persons(id),
|
|
131
|
+
kind text NOT NULL CHECK (kind IN ('rent', 'owned', 'family_home', 'dorm', 'other')),
|
|
132
|
+
residence_state text NOT NULL CHECK (residence_state IN ('planned', 'current', 'ended')),
|
|
133
|
+
city text,
|
|
134
|
+
residence_name text,
|
|
135
|
+
address text,
|
|
136
|
+
move_in_on text CHECK (move_in_on IS NULL OR public.vault_is_partial_date(move_in_on)),
|
|
137
|
+
move_out_on text CHECK (move_out_on IS NULL OR public.vault_is_partial_date(move_out_on)),
|
|
138
|
+
lease_start_on text CHECK (lease_start_on IS NULL OR lease_start_on ~ '^\d{4}-\d{2}-\d{2}$'),
|
|
139
|
+
lease_end_on text CHECK (lease_end_on IS NULL OR lease_end_on ~ '^\d{4}-\d{2}-\d{2}$'),
|
|
140
|
+
landlord_person_id text REFERENCES public.persons(id),
|
|
141
|
+
landlord_or_agency text,
|
|
142
|
+
monthly_rent_cents bigint CHECK (monthly_rent_cents IS NULL OR monthly_rent_cents >= 0),
|
|
143
|
+
deposit_cents bigint CHECK (deposit_cents IS NULL OR deposit_cents >= 0),
|
|
144
|
+
payment_cycle text CHECK (payment_cycle IS NULL OR payment_cycle IN ('monthly', 'quarterly', 'half_yearly', 'yearly')),
|
|
145
|
+
-- 至少要知道“在哪”,否则这一行没有意义;地址不完整时只写城市或小区名,不要猜。
|
|
146
|
+
CHECK (coalesce(nullif(btrim(city), ''), nullif(btrim(residence_name), ''), nullif(btrim(address), '')) IS NOT NULL),
|
|
147
|
+
CHECK (public.vault_partial_date_le(move_in_on, move_out_on)),
|
|
148
|
+
CHECK (lease_start_on IS NULL OR lease_end_on IS NULL OR lease_start_on <= lease_end_on)
|
|
149
|
+
);
|
|
150
|
+
COMMENT ON TABLE public.residences IS '居住史:住过的每一处一行(租房、自有房、老家、宿舍),含租约与房东;取代 Skill 建的 housing_leases';
|
|
151
|
+
COMMENT ON COLUMN public.residences.person_id IS '居住的人,默认本人';
|
|
152
|
+
COMMENT ON COLUMN public.residences.kind IS 'rent=租房,owned=自有住房,family_home=父母家/老家,dorm=宿舍,other=其他';
|
|
153
|
+
COMMENT ON COLUMN public.residences.residence_state IS 'planned=计划入住,current=正在住,ended=已搬离';
|
|
154
|
+
COMMENT ON COLUMN public.residences.city IS '城市';
|
|
155
|
+
COMMENT ON COLUMN public.residences.residence_name IS '小区、楼盘或宿舍名';
|
|
156
|
+
COMMENT ON COLUMN public.residences.address IS '详细地址;不完整就留空,不要猜';
|
|
157
|
+
COMMENT ON COLUMN public.residences.move_in_on IS '实际入住:YYYY、YYYY-MM 或 YYYY-MM-DD';
|
|
158
|
+
COMMENT ON COLUMN public.residences.move_out_on IS '实际搬离,精度同 move_in_on;还在住为空';
|
|
159
|
+
COMMENT ON COLUMN public.residences.lease_start_on IS '租约开始 YYYY-MM-DD(仅租房)';
|
|
160
|
+
COMMENT ON COLUMN public.residences.lease_end_on IS '租约到期 YYYY-MM-DD(仅租房),续租时改这一列';
|
|
161
|
+
COMMENT ON COLUMN public.residences.landlord_person_id IS '房东或管家在人物表中的 id;不是库里的人物时留空,写 landlord_or_agency';
|
|
162
|
+
COMMENT ON COLUMN public.residences.landlord_or_agency IS '房东、管家或中介(文本)';
|
|
163
|
+
COMMENT ON COLUMN public.residences.monthly_rent_cents IS '月租金(分)';
|
|
164
|
+
COMMENT ON COLUMN public.residences.deposit_cents IS '押金(分)';
|
|
165
|
+
COMMENT ON COLUMN public.residences.payment_cycle IS '付款周期:monthly=月付,quarterly=季付,half_yearly=半年付,yearly=年付';
|
|
166
|
+
CREATE INDEX ix_residences_person ON public.residences(person_id, move_in_on);
|
|
167
|
+
|
|
168
|
+
-- 4. 三张表共有的标准列注释与登记。
|
|
169
|
+
DO $$ DECLARE tbl text; item record; BEGIN
|
|
170
|
+
FOREACH tbl IN ARRAY ARRAY['career_history', 'education_history', 'residences'] LOOP
|
|
171
|
+
FOR item IN SELECT * FROM (VALUES
|
|
172
|
+
('id', '行 ID(随机 UUID 文本)'),
|
|
173
|
+
('status', 'proposed=待确认,confirmed=已确认,disputed=用户提出异议,retired=已作废'),
|
|
174
|
+
('confidence', '把握程度:high/medium/low'),
|
|
175
|
+
('sensitivity', '敏感级别:normal/private/intimate'),
|
|
176
|
+
('note', '备注'),
|
|
177
|
+
('provenance', '来源标记:user=用户亲口说的,inferred=Agent 从聊天/录音等推断,import=导入程序写入'),
|
|
178
|
+
('created_at', '创建时间(上海时间)'),
|
|
179
|
+
('created_by', '创建者'),
|
|
180
|
+
('updated_at', '更新时间(上海时间)'),
|
|
181
|
+
('updated_by', '更新者'),
|
|
182
|
+
('deleted_at', '软删除时间;撤回新增该行的批次时填写')
|
|
183
|
+
) AS t(col, note)
|
|
184
|
+
LOOP
|
|
185
|
+
EXECUTE format('COMMENT ON COLUMN public.%I.%I IS %L', tbl, item.col, item.note);
|
|
186
|
+
END LOOP;
|
|
187
|
+
PERFORM public.vault_register_skill_table(tbl);
|
|
188
|
+
END LOOP;
|
|
189
|
+
END $$;
|
|
190
|
+
|
|
191
|
+
-- 5. housing_leases → residences(只在表真实存在且仍是普通表时执行;重复执行或全新库直接跳过)。
|
|
192
|
+
DO $$
|
|
193
|
+
BEGIN
|
|
194
|
+
IF (SELECT c.relkind FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
195
|
+
WHERE n.nspname = 'public' AND c.relname = 'housing_leases') IS DISTINCT FROM 'r' THEN
|
|
196
|
+
RETURN;
|
|
197
|
+
END IF;
|
|
198
|
+
|
|
199
|
+
-- 保留原 id:证据、审计与 Wiki 里引用的行 id 不变,只改表名。
|
|
200
|
+
-- status:active=正在住,ended=已搬离,planned=计划入住;原表没有入住/搬离日,租期原样搬到租约列。
|
|
201
|
+
-- 原表没有 deleted_at(运行时建表时漏了标准列),搬过来一律视为有效行。
|
|
202
|
+
EXECUTE $sql$
|
|
203
|
+
INSERT INTO public.residences (
|
|
204
|
+
id, note, provenance, created_at, created_by, updated_at, updated_by,
|
|
205
|
+
person_id, kind, residence_state, city, residence_name, address,
|
|
206
|
+
lease_start_on, lease_end_on, landlord_or_agency, monthly_rent_cents, deposit_cents, payment_cycle
|
|
207
|
+
)
|
|
208
|
+
SELECT id, note, provenance, created_at, created_by, updated_at, updated_by,
|
|
209
|
+
person_id, 'rent',
|
|
210
|
+
CASE status WHEN 'active' THEN 'current' WHEN 'planned' THEN 'planned' ELSE 'ended' END,
|
|
211
|
+
city, residence_name, address,
|
|
212
|
+
lease_start_on, lease_end_on, landlord_or_agency, monthly_rent_cents, deposit_cents, payment_cycle
|
|
213
|
+
FROM public.housing_leases
|
|
214
|
+
$sql$;
|
|
215
|
+
|
|
216
|
+
UPDATE public.evidence_links SET subject_table = 'residences' WHERE subject_table = 'housing_leases';
|
|
217
|
+
|
|
218
|
+
-- 不另存整表快照:正式迁移不附带备份或数据副本(AGENTS.md 备份执行约束)。原表每一列都已按原 id 搬进 residences,
|
|
219
|
+
-- status 的映射可逆(current↔active),插入也已逐行写进审计,删除原表不丢信息。
|
|
220
|
+
DROP TABLE public.housing_leases;
|
|
221
|
+
|
|
222
|
+
-- 兼容视图:旧查询与 Wiki 视图块里的 housing_leases 继续可读(只读,写入请改 residences)。
|
|
223
|
+
CREATE VIEW public.housing_leases AS
|
|
224
|
+
SELECT id, person_id, city, residence_name, address, landlord_or_agency,
|
|
225
|
+
monthly_rent_cents, deposit_cents, payment_cycle, lease_start_on, lease_end_on,
|
|
226
|
+
CASE residence_state WHEN 'current' THEN 'active' ELSE residence_state END AS status,
|
|
227
|
+
note, provenance, created_at, created_by, updated_at, updated_by
|
|
228
|
+
FROM public.residences
|
|
229
|
+
WHERE kind = 'rent' AND deleted_at IS NULL;
|
|
230
|
+
COMMENT ON VIEW public.housing_leases IS '只读兼容视图:租房记录已并入 residences(kind=rent),新写入请用 residences';
|
|
231
|
+
PERFORM public.vault_register_skill_view('housing_leases');
|
|
232
|
+
END $$;
|
|
233
|
+
|
|
234
|
+
-- 6. intimacy_events.time_precision:与 0159 其他事实表统一的“发生时间精度”。
|
|
235
|
+
-- time_known 保留供现有网页与接口读取(1=知道具体时刻,0=只知道日期)。回填只做有把握的部分:
|
|
236
|
+
-- time_known=0 → date;time_known=1 是建表默认值,无法区分“确实知道时刻”和“没填”,不回填为 exact,留空。
|
|
237
|
+
-- 之后写入方同时给两列时须一致:time_precision 为 date/month 时 time_known=0,为 exact/approximate 时 time_known=1。
|
|
238
|
+
ALTER TABLE public.intimacy_events ADD COLUMN IF NOT EXISTS time_precision text
|
|
239
|
+
CHECK (time_precision IS NULL OR time_precision IN ('exact', 'approximate', 'date', 'month'));
|
|
240
|
+
COMMENT ON COLUMN public.intimacy_events.time_precision IS
|
|
241
|
+
'occurred_at 的可信精度:exact=精确时刻,approximate=约略时刻,date=只知道日期,month=只知道月份;与 time_known 保持一致(date/month ↔ time_known=0)';
|
|
242
|
+
UPDATE public.intimacy_events SET time_precision = 'date'
|
|
243
|
+
WHERE time_known = 0 AND time_precision IS NULL AND deleted_at IS NULL;
|
|
244
|
+
|
|
245
|
+
-- 7. v_typed_orders_explorer 补停车:注释一直写着“外卖/团购/包裹/停车”,但定义里没有停车分支。
|
|
246
|
+
-- 停车记录(ext_parking_orders)挂在支付上(payment_platform + payment_txn_id),不在 orders 里,
|
|
247
|
+
-- 所以用 UNION ALL 追加一段:order_no 列放支付交易号,归属人取支付的 person_id。列名、列序与原视图一致。
|
|
248
|
+
CREATE OR REPLACE VIEW public.v_typed_orders_explorer AS
|
|
249
|
+
SELECT
|
|
250
|
+
o.platform,
|
|
251
|
+
o.order_no,
|
|
252
|
+
coalesce(o.paid_at, o.ordered_at) AS occurred_at,
|
|
253
|
+
round(o.total_paid_cents / 100.0, 2) AS amount_yuan,
|
|
254
|
+
o.status AS order_status,
|
|
255
|
+
CASE
|
|
256
|
+
WHEN t.order_no IS NOT NULL THEN '即时外卖'
|
|
257
|
+
WHEN v.order_no IS NOT NULL THEN '到店团购'
|
|
258
|
+
WHEN p.order_no IS NOT NULL THEN '电商快递'
|
|
259
|
+
WHEN o.shop_name = '火车票' THEN '城际高铁'
|
|
260
|
+
ELSE '其他消费'
|
|
261
|
+
END AS order_category,
|
|
262
|
+
CASE
|
|
263
|
+
WHEN t.order_no IS NOT NULL THEN concat_ws(' · 送至:', t.shop_branch, t.delivery_address)
|
|
264
|
+
WHEN v.order_no IS NOT NULL THEN concat_ws(' · ', v.applicable_mall, v.merchant_name)
|
|
265
|
+
WHEN p.order_no IS NOT NULL THEN concat_ws(' · 收件:', p.carrier, p.shipping_address)
|
|
266
|
+
WHEN o.shop_name = '火车票' THEN '京沪高铁沿线车站'
|
|
267
|
+
ELSE o.shop_name
|
|
268
|
+
END AS consumption_place,
|
|
269
|
+
CASE
|
|
270
|
+
WHEN t.order_no IS NOT NULL THEN t.shop_name
|
|
271
|
+
WHEN v.order_no IS NOT NULL THEN v.package_title
|
|
272
|
+
WHEN p.order_no IS NOT NULL THEN concat_ws(' ', p.goods_name, p.goods_spec)
|
|
273
|
+
WHEN o.shop_name = '火车票' THEN '12306 车票'
|
|
274
|
+
ELSE o.shop_name
|
|
275
|
+
END AS order_detail,
|
|
276
|
+
o.person_id AS owner_person_id
|
|
277
|
+
FROM orders o
|
|
278
|
+
LEFT JOIN ext_takeout_orders t ON t.order_platform = o.platform AND t.order_no = o.order_no
|
|
279
|
+
LEFT JOIN ext_voucher_orders v ON v.order_platform = o.platform AND v.order_no = o.order_no
|
|
280
|
+
LEFT JOIN ext_parcel_orders p ON p.order_platform = o.platform AND p.order_no = o.order_no
|
|
281
|
+
UNION ALL
|
|
282
|
+
SELECT
|
|
283
|
+
k.payment_platform,
|
|
284
|
+
k.payment_txn_id,
|
|
285
|
+
coalesce(k.entry_time, pay.occurred_at),
|
|
286
|
+
round(k.parking_fee_cents / 100.0, 2),
|
|
287
|
+
pay.status,
|
|
288
|
+
'车辆停放',
|
|
289
|
+
concat_ws(' · ', k.city, k.parking_lot_name),
|
|
290
|
+
concat_ws(' ', k.license_plate, CASE WHEN k.duration_minutes IS NOT NULL THEN k.duration_minutes || ' 分钟' END),
|
|
291
|
+
pay.person_id
|
|
292
|
+
FROM ext_parking_orders k
|
|
293
|
+
JOIN payments pay ON pay.platform = k.payment_platform AND pay.txn_id = k.payment_txn_id;
|
|
294
|
+
COMMENT ON VIEW public.v_typed_orders_explorer IS
|
|
295
|
+
'按业务类型(外卖/团购/包裹/高铁/停车)浏览订单;停车来自 ext_parking_orders,挂在支付上,order_no 列为支付交易号';
|
|
296
|
+
|
|
297
|
+
-- 8. 人生时间轴视图:工作、学历、居住合成一条,供 Skill 回答“那几年我在干什么、住哪”与 Wiki 视图块使用。
|
|
298
|
+
CREATE VIEW public.v_life_timeline AS
|
|
299
|
+
SELECT 'career'::text AS lane, id AS source_id, 'career_history'::text AS source_table, person_id,
|
|
300
|
+
concat_ws(' · ', company, title) AS title, concat_ws(' · ', department, city) AS detail,
|
|
301
|
+
start_on, end_on, (end_on IS NULL) AS ongoing
|
|
302
|
+
FROM public.career_history WHERE deleted_at IS NULL AND status <> 'retired'
|
|
303
|
+
UNION ALL
|
|
304
|
+
SELECT 'education', id, 'education_history', person_id,
|
|
305
|
+
concat_ws(' · ', school, major), level, start_on, end_on, graduation_state = 'studying'
|
|
306
|
+
FROM public.education_history WHERE deleted_at IS NULL AND status <> 'retired'
|
|
307
|
+
UNION ALL
|
|
308
|
+
SELECT 'residence', id, 'residences', person_id,
|
|
309
|
+
concat_ws(' · ', city, residence_name), kind, coalesce(move_in_on, lease_start_on),
|
|
310
|
+
coalesce(move_out_on, CASE WHEN residence_state = 'ended' THEN lease_end_on END), residence_state = 'current'
|
|
311
|
+
FROM public.residences WHERE deleted_at IS NULL AND status <> 'retired';
|
|
312
|
+
COMMENT ON VIEW public.v_life_timeline IS
|
|
313
|
+
'人生时间轴:工作(career)、学历(education)、居住(residence)三条泳道合并;日期精度可能只到年或月,ongoing=仍在进行';
|
|
314
|
+
SELECT public.vault_register_skill_view('v_life_timeline');
|