Files
eryao/backend/alembic/versions/20260403_0004_remove_points_reason_code.py
T

317 lines
10 KiB
Python
Raw Normal View History

2026-04-03 16:56:47 +08:00
"""remove redundant reason_code from points ledger metadata
Revision ID: 202604030004
Revises: 202604030003
Create Date: 2026-04-03 23:35:00
"""
from typing import Sequence, Union
from alembic import op
revision: str = "202604030004"
down_revision: Union[str, Sequence[str], None] = "202604030003"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.drop_constraint(
"ck_points_ledger_metadata_common", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_register_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_consume_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_grant_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_adjust_shape", "points_ledger", type_="check"
)
op.create_check_constraint(
"ck_points_ledger_metadata_common",
"points_ledger",
"metadata->>'schema_version' = '1' and "
"metadata->>'operator_type' in ('user', 'system', 'admin') and "
"coalesce(metadata->>'run_id', '') <> '' and "
"(not (metadata ? 'ext') or jsonb_typeof(metadata->'ext') = 'object')",
)
op.create_check_constraint(
"ck_points_ledger_metadata_register_shape",
"points_ledger",
"(change_type <> 'register' or not (metadata ? 'charge'))",
)
op.create_check_constraint(
"ck_points_ledger_metadata_consume_shape",
"points_ledger",
"(change_type <> 'consume' or ("
"(metadata ? 'charge') and jsonb_typeof(metadata->'charge') = 'object' and "
"(metadata->'charge' ? 'message_id') and (metadata->'charge' ? 'message_seq') and "
"(metadata->'charge' ? 'model_code') and (metadata->'charge' ? 'input_tokens') and "
"(metadata->'charge' ? 'output_tokens') and (metadata->'charge' ? 'cost')))",
)
op.create_check_constraint(
"ck_points_ledger_metadata_adjust_shape",
"points_ledger",
"(change_type <> 'adjust' or ("
"(metadata ? 'ext') and (metadata->'ext' ? 'ticket_id') and "
"coalesce(metadata #>> '{ext,ticket_id}', '') <> ''))",
)
op.execute(
"update points_ledger set metadata = metadata - 'reason_code' where metadata ? 'reason_code'"
)
op.execute(
"""
create or replace function public.initialize_profile_and_points_on_signup()
returns trigger
language plpgsql
security definer
set search_path = public
as $$
declare
v_username text;
v_ledger_id uuid;
v_event_id text;
begin
v_username := 'user_' || substring(md5(new.id::text || clock_timestamp()::text || random()::text) from 1 for 6);
v_ledger_id := md5(new.id::text || 'ledger' || clock_timestamp()::text || random()::text)::uuid;
v_event_id := 'register:' || new.id::text;
insert into public.profiles (id, username, avatar_url, bio, settings)
values (
new.id,
v_username,
null,
null,
jsonb_build_object(
'version', 1,
'preferences', jsonb_build_object(
'interface_language', 'zh-CN',
'ai_language', 'zh-CN',
'timezone', 'Asia/Shanghai',
'country', 'CN'
),
'privacy', jsonb_build_object('profile_visibility', 'public'),
'notification', jsonb_build_object('push_enabled', true)
)
)
on conflict (id) do nothing;
insert into public.user_points (
user_id,
balance,
frozen_balance,
lifetime_earned,
lifetime_spent,
version
)
values (new.id, 60, 0, 60, 0, 0)
2026-04-03 16:56:47 +08:00
on conflict (user_id) do nothing;
insert into public.points_ledger (
id,
user_id,
direction,
amount,
balance_after,
change_type,
biz_type,
biz_id,
event_id,
operator_id,
metadata
)
values (
v_ledger_id,
new.id,
1,
60,
60,
2026-04-03 16:56:47 +08:00
'register',
null,
null,
v_event_id,
null,
jsonb_build_object(
'schema_version', 1,
'operator_type', 'system',
'run_id', v_event_id,
'request_id', null,
'ext', jsonb_build_object('source', 'auth_signup')
)
)
on conflict (user_id, event_id) do nothing;
return new;
end;
$$;
"""
)
def downgrade() -> None:
op.drop_constraint(
"ck_points_ledger_metadata_adjust_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_consume_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_register_shape", "points_ledger", type_="check"
)
op.drop_constraint(
"ck_points_ledger_metadata_common", "points_ledger", type_="check"
)
op.execute(
"""
update points_ledger
set metadata = jsonb_set(
metadata,
'{reason_code}',
to_jsonb(
case change_type
when 'register' then 'REGISTER_WELCOME'
when 'consume' then 'CHAT_CONSUME'
when 'grant' then 'CHAT_GRANT'
when 'adjust' then 'CHAT_ADJUST'
else 'CHAT_ADJUST'
end
),
true
)
where not (metadata ? 'reason_code');
"""
)
op.create_check_constraint(
"ck_points_ledger_metadata_common",
"points_ledger",
"metadata->>'schema_version' = '1' and "
"metadata->>'reason_code' in ('REGISTER_WELCOME', 'CHAT_CONSUME', 'CHAT_GRANT', 'CHAT_ADJUST') and "
"metadata->>'operator_type' in ('user', 'system', 'admin') and "
"coalesce(metadata->>'run_id', '') <> '' and "
"(not (metadata ? 'ext') or jsonb_typeof(metadata->'ext') = 'object')",
)
op.create_check_constraint(
"ck_points_ledger_metadata_register_shape",
"points_ledger",
"(change_type <> 'register' or (metadata->>'reason_code' = 'REGISTER_WELCOME' and not (metadata ? 'charge')))",
)
op.create_check_constraint(
"ck_points_ledger_metadata_consume_shape",
"points_ledger",
"(change_type <> 'consume' or (metadata->>'reason_code' = 'CHAT_CONSUME' and "
"(metadata ? 'charge') and jsonb_typeof(metadata->'charge') = 'object' and "
"(metadata->'charge' ? 'message_id') and (metadata->'charge' ? 'message_seq') and "
"(metadata->'charge' ? 'model_code') and (metadata->'charge' ? 'input_tokens') and "
"(metadata->'charge' ? 'output_tokens') and (metadata->'charge' ? 'cost')))",
)
op.create_check_constraint(
"ck_points_ledger_metadata_grant_shape",
"points_ledger",
"(change_type <> 'grant' or metadata->>'reason_code' = 'CHAT_GRANT')",
)
op.create_check_constraint(
"ck_points_ledger_metadata_adjust_shape",
"points_ledger",
"(change_type <> 'adjust' or (metadata->>'reason_code' = 'CHAT_ADJUST' and "
"(metadata ? 'ext') and (metadata->'ext' ? 'ticket_id') and "
"coalesce(metadata #>> '{ext,ticket_id}', '') <> ''))",
)
op.execute(
"""
create or replace function public.initialize_profile_and_points_on_signup()
returns trigger
language plpgsql
security definer
set search_path = public
as $$
declare
v_username text;
v_ledger_id uuid;
v_event_id text;
begin
v_username := 'user_' || substring(md5(new.id::text || clock_timestamp()::text || random()::text) from 1 for 6);
v_ledger_id := md5(new.id::text || 'ledger' || clock_timestamp()::text || random()::text)::uuid;
v_event_id := 'register:' || new.id::text;
insert into public.profiles (id, username, avatar_url, bio, settings)
values (
new.id,
v_username,
null,
null,
jsonb_build_object(
'version', 1,
'preferences', jsonb_build_object(
'interface_language', 'zh-CN',
'ai_language', 'zh-CN',
'timezone', 'Asia/Shanghai',
'country', 'CN'
),
'privacy', jsonb_build_object('profile_visibility', 'public'),
'notification', jsonb_build_object('push_enabled', true)
)
)
on conflict (id) do nothing;
insert into public.user_points (
user_id,
balance,
frozen_balance,
lifetime_earned,
lifetime_spent,
version
)
values (new.id, 60, 0, 60, 0, 0)
2026-04-03 16:56:47 +08:00
on conflict (user_id) do nothing;
insert into public.points_ledger (
id,
user_id,
direction,
amount,
balance_after,
change_type,
biz_type,
biz_id,
event_id,
operator_id,
metadata
)
values (
v_ledger_id,
new.id,
1,
60,
60,
2026-04-03 16:56:47 +08:00
'register',
null,
null,
v_event_id,
null,
jsonb_build_object(
'schema_version', 1,
'reason_code', 'REGISTER_WELCOME',
'operator_type', 'system',
'run_id', v_event_id,
'request_id', null,
'ext', jsonb_build_object('source', 'auth_signup')
)
)
on conflict (user_id, event_id) do nothing;
return new;
end;
$$;
"""
)