- settlement(1.1): exhibition_revenue_records 加 UNIQUE(exhibition_id,cycle_start_time) + CreateRevenueRecord ON CONFLICT DO NOTHING; MQ 用 settled_at 替代复用 is_processed; 恢复扫描器过滤改 settled_at IS NULL; created_at 统一毫秒; 删死代码 cleanup_worker.go。 - hours(1.2): 新增 exhibition_hours_log/asset_exhibition_hours_log(source_id 唯一)幂等表; fan_profile/assetLevel 的 AddExhibitionHours 按 sourceID 幂等(事务包裹); 存量重算脚本。 - mint(1.4/1.5/1.6): crystal_transaction_records (source_id,change_type) 部分唯一索引 + UpdateCrystalBalance/CreateMintOrder 幂等; 保底改 crypto/rand; 下线伪 tx_hash; doMint Redis Lua 原子限流。 - migrations 001/002/003; 各服务单测(自包含, 缺 DB t.Skip)。 Co-Authored-By: Claude <noreply@anthropic.com>
173 lines
8.0 KiB
SQL
173 lines
8.0 KiB
SQL
-- recalc_exhibition_hours.sql
|
|
-- 批次1.2 存量校正(DRY-RUN): 输出"按 distinct exhibition 重算"前后的差异
|
|
-- ⚠️ 本文件仅 SELECT,禁止任何 DDL/DML。
|
|
--
|
|
-- 列名适配说明:
|
|
-- * exhibitions.slot_owner_uid -> 实际为 host_profile_id
|
|
-- * 收益归属(revenue_service.go L356): user_id=SlotOwnerUID(=host_profile_id), star_id=OccupierStarID
|
|
-- * level_up_bonus 触发: AddExhibitionHours(userID=host_profile_id, starID=occupier_star_id)
|
|
|
|
\echo '==== A. 用户级:当前 vs 重算 (差值 > 0 即虚高) ===='
|
|
WITH per_user_star AS (
|
|
SELECT e.host_profile_id AS user_id,
|
|
e.occupier_star_id AS star_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL
|
|
AND e.host_profile_id IS NOT NULL
|
|
AND e.occupier_star_id IS NOT NULL
|
|
GROUP BY e.host_profile_id, e.occupier_star_id
|
|
),
|
|
current_ueh AS (
|
|
SELECT user_id, star_id, total_exhibition_hours AS current_hours
|
|
FROM user_exhibition_hours
|
|
)
|
|
SELECT COALESCE(c.user_id, r.user_id) AS user_id,
|
|
COALESCE(c.star_id, r.star_id) AS star_id,
|
|
COALESCE(c.current_hours, 0) AS current_hours,
|
|
COALESCE(r.recalc_hours, 0) AS recalc_hours,
|
|
(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) AS diff_hours
|
|
FROM current_ueh c
|
|
FULL OUTER JOIN per_user_star r USING (user_id, star_id)
|
|
WHERE COALESCE(c.current_hours, 0) <> COALESCE(r.recalc_hours, 0)
|
|
ORDER BY ABS(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) DESC
|
|
LIMIT 50;
|
|
|
|
\echo '==== B. 用户级 diff 汇总 (待下修/虚高,无 DML) ===='
|
|
WITH per_user_star AS (
|
|
SELECT e.host_profile_id AS user_id,
|
|
e.occupier_star_id AS star_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL
|
|
AND e.host_profile_id IS NOT NULL
|
|
AND e.occupier_star_id IS NOT NULL
|
|
GROUP BY e.host_profile_id, e.occupier_star_id
|
|
)
|
|
SELECT count(*) FILTER (WHERE (COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) > 0) AS user_star_pairs_overstated,
|
|
count(*) FILTER (WHERE (COALESCE(c.current_hours, 0) - COALESCE(c.current_hours, 0)) > 0) AS placeholder_no_overcount,
|
|
count(*) FILTER (WHERE (COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) < 0) AS user_star_pairs_understated,
|
|
count(*) FILTER (WHERE c.user_id IS NULL) AS user_star_pairs_in_exhibition_only,
|
|
count(*) FILTER (WHERE r.user_id IS NULL) AS user_star_pairs_in_ueh_only,
|
|
COALESCE(sum(GREATEST(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0), 0)), 0) AS total_overstated_hours,
|
|
COALESCE(sum(GREATEST(COALESCE(r.recalc_hours, 0) - COALESCE(c.current_hours, 0), 0)), 0) AS total_understated_hours
|
|
FROM (SELECT user_id, star_id, total_exhibition_hours AS current_hours FROM user_exhibition_hours) c
|
|
FULL OUTER JOIN per_user_star r USING (user_id, star_id);
|
|
|
|
\echo '==== C. 资产级:当前 vs 重算 (差值 > 0 即虚高) ===='
|
|
WITH per_asset AS (
|
|
SELECT e.asset_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL AND e.asset_id > 0
|
|
GROUP BY e.asset_id
|
|
)
|
|
SELECT COALESCE(c.asset_id, r.asset_id) AS asset_id,
|
|
COALESCE(c.current_hours, 0) AS current_hours,
|
|
COALESCE(r.recalc_hours, 0) AS recalc_hours,
|
|
(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) AS diff_hours
|
|
FROM (
|
|
SELECT asset_id, season_exhibition_hours AS current_hours
|
|
FROM asset_level_records WHERE season_exhibition_hours > 0
|
|
) c
|
|
FULL OUTER JOIN per_asset r USING (asset_id)
|
|
WHERE COALESCE(c.current_hours, 0) <> COALESCE(r.recalc_hours, 0)
|
|
ORDER BY ABS(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) DESC
|
|
LIMIT 50;
|
|
|
|
\echo '==== D. 资产级 diff 汇总 ===='
|
|
WITH per_asset AS (
|
|
SELECT e.asset_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL AND e.asset_id > 0
|
|
GROUP BY e.asset_id
|
|
)
|
|
SELECT count(*) FILTER (WHERE (COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) > 0) AS assets_overstated,
|
|
count(*) FILTER (WHERE (COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0)) < 0) AS assets_understated,
|
|
count(*) FILTER (WHERE c.asset_id IS NULL) AS assets_in_exhibition_only,
|
|
count(*) FILTER (WHERE r.asset_id IS NULL) AS assets_in_alr_only,
|
|
COALESCE(sum(GREATEST(COALESCE(c.current_hours, 0) - COALESCE(r.recalc_hours, 0), 0)), 0) AS total_overstated_asset_hours,
|
|
COALESCE(sum(GREATEST(COALESCE(r.recalc_hours, 0) - COALESCE(c.current_hours, 0), 0)), 0) AS total_understated_asset_hours
|
|
FROM (SELECT asset_id, season_exhibition_hours AS current_hours FROM asset_level_records WHERE season_exhibition_hours > 0) c
|
|
FULL OUTER JOIN per_asset r USING (asset_id);
|
|
|
|
\echo '==== E. 已发升级奖励水晶总额 (change_type=level_up_bonus, 仅统计) ===='
|
|
SELECT count(*) AS level_up_bonus_records,
|
|
COALESCE(sum(delta), 0) AS total_crystal
|
|
FROM crystal_transaction_records
|
|
WHERE change_type = 'level_up_bonus';
|
|
|
|
\echo '==== F. level_up_bonus 按 source_id 是否形如 exhibition_<id> 拆分 ===='
|
|
SELECT
|
|
count(*) FILTER (WHERE source_id ~ '^exhibition_[0-9]+$') AS looks_like_exhibition_source,
|
|
count(*) FILTER (WHERE source_id IS NULL OR source_id = '') AS no_source_id,
|
|
count(*) FILTER (WHERE source_id IS NOT NULL AND source_id <> '' AND source_id !~ '^exhibition_[0-9]+$') AS other_source,
|
|
COALESCE(sum(delta) FILTER (WHERE source_id ~ '^exhibition_[0-9]+$'), 0) AS exhibition_source_crystal,
|
|
COALESCE(sum(delta) FILTER (WHERE source_id IS NULL OR source_id = ''), 0) AS no_source_crystal
|
|
FROM crystal_transaction_records
|
|
WHERE change_type = 'level_up_bonus';
|
|
|
|
\echo '==== G. 误升用户数: fan_profiles 当前 level vs 按 recalc_hours 应得 level ===='
|
|
WITH per_user_star AS (
|
|
SELECT e.host_profile_id AS user_id,
|
|
e.occupier_star_id AS star_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL
|
|
AND e.host_profile_id IS NOT NULL
|
|
AND e.occupier_star_id IS NOT NULL
|
|
GROUP BY e.host_profile_id, e.occupier_star_id
|
|
),
|
|
expected_level AS (
|
|
SELECT lt.level AS expected_level, r.user_id, r.star_id, r.recalc_hours
|
|
FROM per_user_star r
|
|
JOIN LATERAL (
|
|
SELECT level FROM level_thresholds
|
|
WHERE max_exhibition_hours <= r.recalc_hours
|
|
ORDER BY level DESC LIMIT 1
|
|
) lt ON true
|
|
)
|
|
SELECT count(*) FILTER (WHERE fp.level > el.expected_level) AS profiles_will_downgrade,
|
|
count(*) FILTER (WHERE fp.level < el.expected_level) AS profiles_will_upgrade,
|
|
count(*) FILTER (WHERE fp.level = el.expected_level) AS profiles_match,
|
|
COALESCE(sum(fp.level - el.expected_level) FILTER (WHERE fp.level > el.expected_level), 0) AS total_level_reduction
|
|
FROM fan_profiles fp
|
|
JOIN expected_level el
|
|
ON fp.user_id = el.user_id AND fp.star_id = el.star_id;
|
|
|
|
\echo '==== H. 误升用户关联的 level_up_bonus 总额 (若 fan_profile 当前 level > recalc 应得 level) ===='
|
|
WITH per_user_star AS (
|
|
SELECT e.host_profile_id AS user_id,
|
|
e.occupier_star_id AS star_id,
|
|
SUM((e.expire_at - e.start_time) / 3600000)::bigint AS recalc_hours
|
|
FROM exhibitions e
|
|
WHERE e.deleted_at IS NULL
|
|
AND e.host_profile_id IS NOT NULL
|
|
AND e.occupier_star_id IS NOT NULL
|
|
GROUP BY e.host_profile_id, e.occupier_star_id
|
|
),
|
|
expected_level AS (
|
|
SELECT r.user_id, r.star_id, lt.level AS expected_level
|
|
FROM per_user_star r
|
|
JOIN LATERAL (
|
|
SELECT level FROM level_thresholds
|
|
WHERE max_exhibition_hours <= r.recalc_hours
|
|
ORDER BY level DESC LIMIT 1
|
|
) lt ON true
|
|
),
|
|
mis_upgraded_users AS (
|
|
SELECT fp.user_id, fp.star_id
|
|
FROM fan_profiles fp
|
|
JOIN expected_level el
|
|
ON fp.user_id = el.user_id AND fp.star_id = el.star_id
|
|
WHERE fp.level > el.expected_level
|
|
)
|
|
SELECT count(*) AS mis_upgrade_crystal_records,
|
|
COALESCE(sum(delta), 0) AS mis_upgrade_total_crystal
|
|
FROM crystal_transaction_records ctr
|
|
WHERE ctr.change_type = 'level_up_bonus'
|
|
AND EXISTS (
|
|
SELECT 1 FROM mis_upgraded_users m
|
|
WHERE m.user_id = ctr.user_id AND m.star_id = ctr.star_id
|
|
); |