-- +goose Up -- 双边积分与虚拟积分商城。 ALTER TABLE users ADD COLUMN points_available BIGINT NOT NULL DEFAULT 0 COMMENT '积分商城可用积分' AFTER renter_growth_level; UPDATE users SET points_available = renter_growth_points WHERE points_available = 0 AND renter_growth_points > 0; ALTER TABLE rental_orders ADD COLUMN growth_points_multiplier_bps INT NOT NULL DEFAULT 10000 COMMENT '租客积分倍率基点' AFTER growth_points_per_yuan, ADD COLUMN owner_growth_points_basis_cent BIGINT NOT NULL DEFAULT 0 COMMENT '卖家积分基数(分)' AFTER growth_points_awarded_at, ADD COLUMN owner_growth_points_per_yuan BIGINT NOT NULL DEFAULT 0 COMMENT '卖家基础每元积分' AFTER owner_growth_points_basis_cent, ADD COLUMN owner_growth_points_multiplier_bps INT NOT NULL DEFAULT 10000 COMMENT '卖家积分倍率基点' AFTER owner_growth_points_per_yuan, ADD COLUMN owner_growth_points_awarded BIGINT NOT NULL DEFAULT 0 COMMENT '卖家已发放积分' AFTER owner_growth_points_multiplier_bps, ADD COLUMN owner_growth_points_awarded_at DATETIME NULL COMMENT '卖家积分发放时间' AFTER owner_growth_points_awarded; ALTER TABLE renter_growth_ledger DROP KEY uk_renter_growth_order_source, ADD COLUMN role VARCHAR(16) NOT NULL DEFAULT 'renter' COMMENT '积分角色: renter/owner/admin' AFTER user_id, ADD COLUMN points_multiplier_bps INT NOT NULL DEFAULT 10000 COMMENT '积分倍率基点' AFTER points_per_yuan, ADD COLUMN available_before BIGINT NOT NULL DEFAULT 0 COMMENT '变动前可用积分' AFTER after_points, ADD COLUMN available_after BIGINT NOT NULL DEFAULT 0 COMMENT '变动后可用积分' AFTER available_before, ADD COLUMN redemption_id BIGINT NOT NULL DEFAULT 0 COMMENT '积分商城兑换单 ID' AFTER order_id, ADD COLUMN biz_key VARCHAR(128) NULL COMMENT '幂等业务键' AFTER source, ADD UNIQUE KEY uk_renter_growth_biz_key (biz_key), ADD UNIQUE KEY uk_renter_growth_order_role_source (order_id, source, role), ADD KEY idx_renter_growth_user_role_created (user_id, role, created_at); CREATE TABLE IF NOT EXISTS points_mall_products ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(128) NOT NULL, subtitle VARCHAR(255) NOT NULL DEFAULT '', description TEXT NOT NULL, cover_url VARCHAR(512) NOT NULL DEFAULT '', redemption_instructions TEXT NOT NULL, points_price BIGINT NOT NULL DEFAULT 0, stock BIGINT NOT NULL DEFAULT 0, redeemed_count BIGINT NOT NULL DEFAULT 0, per_user_limit INT NOT NULL DEFAULT 1, status VARCHAR(16) NOT NULL DEFAULT 'offline', sort_order INT NOT NULL DEFAULT 0, created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (id), KEY idx_points_mall_products_status_sort (status, sort_order, id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城虚拟商品'; CREATE TABLE IF NOT EXISTS points_mall_redemptions ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, redemption_no VARCHAR(32) NOT NULL, user_id BIGINT UNSIGNED NOT NULL, product_id BIGINT UNSIGNED NOT NULL, product_title VARCHAR(128) NOT NULL, product_cover_url VARCHAR(512) NOT NULL DEFAULT '', redemption_instructions TEXT NOT NULL, contact_name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '兑换联系人', contact_phone VARCHAR(32) NOT NULL DEFAULT '' COMMENT '兑换联系人手机号', customer_remark VARCHAR(500) NOT NULL DEFAULT '' COMMENT '用户兑换备注', points_price BIGINT NOT NULL DEFAULT 0, quantity INT NOT NULL DEFAULT 1, total_points BIGINT NOT NULL DEFAULT 0, status VARCHAR(16) NOT NULL DEFAULT 'pending', admin_remark VARCHAR(255) NOT NULL DEFAULT '', completed_by BIGINT UNSIGNED NULL, completed_at DATETIME NULL, created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (id), UNIQUE KEY uk_points_mall_redemption_no (redemption_no), KEY idx_points_mall_redemptions_user (user_id, created_at), KEY idx_points_mall_redemptions_status (status, created_at), KEY idx_points_mall_redemptions_product (product_id, user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城兑换记录'; INSERT INTO permissions (code, name, resource, action) VALUES ('points_mall:view', '查看积分商城', 'points_mall', 'view'), ('points_mall:manage', '管理积分商城', 'points_mall', 'manage') ON DUPLICATE KEY UPDATE name = VALUES(name), resource = VALUES(resource), action = VALUES(action); INSERT IGNORE INTO role_permissions (role_id, permission_id) SELECT r.id, p.id FROM roles r JOIN permissions p ON p.code IN ('points_mall:view', 'points_mall:manage') WHERE r.code IN ('ops', 'super_admin'); -- +goose Down DELETE rp FROM role_permissions rp JOIN permissions p ON p.id = rp.permission_id WHERE p.code IN ('points_mall:view', 'points_mall:manage'); DELETE FROM permissions WHERE code IN ('points_mall:view', 'points_mall:manage'); DROP TABLE IF EXISTS points_mall_redemptions; DROP TABLE IF EXISTS points_mall_products; ALTER TABLE rental_orders DROP COLUMN owner_growth_points_awarded_at, DROP COLUMN owner_growth_points_awarded, DROP COLUMN owner_growth_points_multiplier_bps, DROP COLUMN owner_growth_points_per_yuan, DROP COLUMN owner_growth_points_basis_cent, DROP COLUMN growth_points_multiplier_bps; ALTER TABLE renter_growth_ledger DROP KEY idx_renter_growth_user_role_created, DROP KEY uk_renter_growth_order_role_source, DROP KEY uk_renter_growth_biz_key, DROP COLUMN biz_key, DROP COLUMN redemption_id, DROP COLUMN available_after, DROP COLUMN available_before, DROP COLUMN points_multiplier_bps, DROP COLUMN role, ADD UNIQUE KEY uk_renter_growth_order_source (order_id, source); ALTER TABLE users DROP COLUMN points_available;