owpengram-server/deploy/migrations/0105_star_gift_craft_projection.up.sql
A 14bf7d1e20 feat: sync official star gift lifecycle tooling
Sync telesrv 4c0e2d9 (feat: complete official star gift lifecycle and admin tooling).

Public adjustments: skipped private docs/deploy-nginx/README source changes, kept iamxvbaba/td public dependency, replaced local/private sample IP and orange seed label.
2026-07-19 03:11:35 +08:00

195 lines
9.1 KiB
SQL

-- Craft outcomes consume their inputs permanently. Keep the aggregate, every
-- visible messageActionStarGiftUnique snapshot, pts/outbox delivery and command
-- replay receipt in one state model.
ALTER TABLE public.star_gift_craft_commands
ADD COLUMN source_edit_pts integer[] DEFAULT ARRAY[]::integer[] NOT NULL;
DO $$
DECLARE
gift_box record;
next_pts integer;
event_date integer := EXTRACT(EPOCH FROM clock_timestamp())::integer;
repaired_media jsonb;
BEGIN
-- A successful legacy command would require reconstructing the crafted
-- model snapshot, not merely flipping flags. Fail fast instead of silently
-- fabricating that projection; the development database must be rebuilt or
-- repaired explicitly if such a row ever exists.
IF EXISTS (SELECT 1 FROM public.star_gift_craft_commands WHERE success) THEN
RAISE EXCEPTION 'cannot migrate legacy successful craft command without an exact crafted message projection';
END IF;
-- upgrade_msg_id means the current owner's unique service-message
-- projection, not permanently the first upgrade message. Ownership moves
-- replace msg_id with the new transfer/resale/offer message; repair rows
-- written before that invariant was enforced.
UPDATE public.peer_star_gifts saved_gift
SET upgrade_msg_id = saved_gift.msg_id
WHERE saved_gift.owner_peer_type = 'user'
AND saved_gift.unique_gift_id IS NOT NULL
AND saved_gift.msg_id > 0
AND EXISTS (
SELECT 1
FROM public.message_boxes box
WHERE box.owner_user_id = saved_gift.owner_peer_id
AND box.box_id = saved_gift.msg_id
AND NOT box.deleted
AND box.media #>> '{service_action,kind}' = 'star_gift_unique'
AND (box.media #>> '{service_action,star_gift_unique,gift,ID}')::bigint = saved_gift.unique_gift_id
);
UPDATE public.peer_star_gifts
SET upgrade_msg_id = 0
WHERE owner_peer_type = 'channel'
AND unique_gift_id IS NOT NULL
AND upgrade_msg_id <> 0;
UPDATE public.unique_star_gifts
SET burned = true,
craft_chance_permille = 0,
offer_min_stars = 0,
updated_at = now()
WHERE burned;
UPDATE public.peer_star_gifts
SET lifecycle_status = 'burned',
unsaved = true,
pinned_order = 0,
transfer_stars = 0,
can_export_at = 0,
can_transfer_at = 0,
can_resell_at = 0,
drop_original_details_stars = 0,
can_craft_at = 0
WHERE lifecycle_status = 'burned';
FOR gift_box IN
SELECT box.owner_user_id,
box.box_id,
box.peer_type,
box.peer_id,
box.message_sender_id,
box.private_message_id,
box.media
FROM public.message_boxes box
JOIN public.unique_star_gifts unique_gift
ON unique_gift.id = (box.media #>> '{service_action,star_gift_unique,gift,ID}')::bigint
WHERE box.media #>> '{service_action,kind}' = 'star_gift_unique'
AND unique_gift.burned
AND (
COALESCE((box.media #>> '{service_action,star_gift_unique,gift,Burned}')::boolean, false) = false
OR COALESCE((box.media #>> '{service_action,star_gift_unique,gift,CraftChancePermille}')::integer, 0) <> 0
OR box.media #> '{service_action,star_gift_unique,saved}' IS NOT NULL
OR box.media #> '{service_action,star_gift_unique,can_craft_at}' IS NOT NULL
)
ORDER BY box.owner_user_id, box.box_id
LOOP
repaired_media := jsonb_set(
jsonb_set(
gift_box.media
#- '{service_action,star_gift_unique,gift,CraftChancePermille}'
#- '{service_action,star_gift_unique,saved}'
#- '{service_action,star_gift_unique,can_export_at}'
#- '{service_action,star_gift_unique,transfer_stars}'
#- '{service_action,star_gift_unique,resale_amount}'
#- '{service_action,star_gift_unique,can_transfer_at}'
#- '{service_action,star_gift_unique,can_resell_at}'
#- '{service_action,star_gift_unique,drop_original_details_stars}'
#- '{service_action,star_gift_unique,can_craft_at}',
'{service_action,star_gift_unique,gift,Burned}', 'true'::jsonb, true),
'{service_action,star_gift_unique,gift,OfferMinStars}', '0'::jsonb, true);
INSERT INTO public.user_update_watermarks (user_id, contiguous_pts)
VALUES (gift_box.owner_user_id, 0)
ON CONFLICT (user_id) DO NOTHING;
UPDATE public.user_update_watermarks
SET contiguous_pts = contiguous_pts + 1,
updated_at = now()
WHERE user_id = gift_box.owner_user_id
RETURNING contiguous_pts INTO next_pts;
UPDATE public.message_boxes
SET media = repaired_media,
pts = next_pts
WHERE owner_user_id = gift_box.owner_user_id
AND box_id = gift_box.box_id
AND NOT deleted;
UPDATE public.private_messages
SET media = jsonb_set(
jsonb_set(
media
#- '{service_action,star_gift_unique,gift,CraftChancePermille}'
#- '{service_action,star_gift_unique,saved}'
#- '{service_action,star_gift_unique,can_export_at}'
#- '{service_action,star_gift_unique,transfer_stars}'
#- '{service_action,star_gift_unique,resale_amount}'
#- '{service_action,star_gift_unique,can_transfer_at}'
#- '{service_action,star_gift_unique,can_resell_at}'
#- '{service_action,star_gift_unique,drop_original_details_stars}'
#- '{service_action,star_gift_unique,can_craft_at}',
'{service_action,star_gift_unique,gift,Burned}', 'true'::jsonb, true),
'{service_action,star_gift_unique,gift,OfferMinStars}', '0'::jsonb, true),
sender_snapshot = jsonb_set(
jsonb_set(
sender_snapshot
#- '{message,Media,service_action,star_gift_unique,gift,CraftChancePermille}'
#- '{message,Media,service_action,star_gift_unique,saved}'
#- '{message,Media,service_action,star_gift_unique,can_export_at}'
#- '{message,Media,service_action,star_gift_unique,transfer_stars}'
#- '{message,Media,service_action,star_gift_unique,resale_amount}'
#- '{message,Media,service_action,star_gift_unique,can_transfer_at}'
#- '{message,Media,service_action,star_gift_unique,can_resell_at}'
#- '{message,Media,service_action,star_gift_unique,drop_original_details_stars}'
#- '{message,Media,service_action,star_gift_unique,can_craft_at}',
'{message,Media,service_action,star_gift_unique,gift,Burned}', 'true'::jsonb, true),
'{message,Media,service_action,star_gift_unique,gift,OfferMinStars}', '0'::jsonb, true)
WHERE sender_user_id = gift_box.message_sender_id
AND id = gift_box.private_message_id;
INSERT INTO public.user_update_events (
user_id, pts, pts_count, date, event_type,
message_box_id, peer_type, peer_id
) VALUES (
gift_box.owner_user_id, next_pts, 1, event_date, 'edit_message',
gift_box.box_id, gift_box.peer_type, gift_box.peer_id
);
INSERT INTO public.dispatch_outbox (
target_user_id, pts, event_type,
exclude_auth_key_id, exclude_session_id
) VALUES (
gift_box.owner_user_id, next_pts, 'edit_message', 0, 0
);
END LOOP;
UPDATE public.star_gift_craft_commands command
SET source_edit_pts = (
SELECT array_agg(box.pts ORDER BY input.ordinality)::integer[] AS pts
FROM unnest(command.input_unique_gift_ids) WITH ORDINALITY AS input(unique_gift_id, ordinality)
JOIN public.unique_star_gifts unique_gift ON unique_gift.id = input.unique_gift_id
JOIN public.peer_star_gifts saved_gift ON saved_gift.id = unique_gift.source_saved_gift_id
JOIN public.message_boxes box
ON box.owner_user_id = command.user_id
AND box.box_id = saved_gift.upgrade_msg_id
AND NOT box.deleted
)
WHERE NOT command.success;
IF EXISTS (
SELECT 1
FROM public.star_gift_craft_commands
WHERE cardinality(source_edit_pts) <> cardinality(input_unique_gift_ids)
OR array_position(source_edit_pts, 0) IS NOT NULL
) THEN
RAISE EXCEPTION 'craft command is missing an exact source message edit receipt';
END IF;
END
$$;
ALTER TABLE public.star_gift_craft_commands
ADD CONSTRAINT star_gift_craft_source_edit_shape_check CHECK (
cardinality(source_edit_pts) = cardinality(input_unique_gift_ids)
AND array_position(source_edit_pts, 0) IS NULL
);