238 lines
13 KiB
SQL
238 lines
13 KiB
SQL
-- Author: fruit.com.cn
|
|
-- 奖励中心:任务配置、签到、进度、发奖、活动与后台菜单(PostgreSQL,幂等)。
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_task (
|
|
id BIGINT PRIMARY KEY,
|
|
code VARCHAR(64) NOT NULL,
|
|
section_code VARCHAR(32) NOT NULL DEFAULT 'NEWBIE',
|
|
type VARCHAR(32) NOT NULL,
|
|
verify_mode VARCHAR(16) NOT NULL DEFAULT 'SERVER',
|
|
target_value BIGINT NOT NULL DEFAULT 1,
|
|
reward_coin BIGINT NOT NULL DEFAULT 0,
|
|
cycle_type VARCHAR(16) NOT NULL DEFAULT 'ONCE',
|
|
cycle_limit INT NOT NULL DEFAULT 1,
|
|
action_type VARCHAR(32) NOT NULL DEFAULT 'NONE',
|
|
action_url VARCHAR(512) NOT NULL DEFAULT '',
|
|
icon_url VARCHAR(512) NOT NULL DEFAULT '',
|
|
title_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
title_en VARCHAR(128) NOT NULL DEFAULT '',
|
|
title_zh VARCHAR(128) NOT NULL DEFAULT '',
|
|
subtitle_id VARCHAR(255) NOT NULL DEFAULT '',
|
|
subtitle_en VARCHAR(255) NOT NULL DEFAULT '',
|
|
subtitle_zh VARCHAR(255) NOT NULL DEFAULT '',
|
|
cta_id VARCHAR(64) NOT NULL DEFAULT '',
|
|
cta_en VARCHAR(64) NOT NULL DEFAULT '',
|
|
cta_zh VARCHAR(64) NOT NULL DEFAULT '',
|
|
section_title_id VARCHAR(64) NOT NULL DEFAULT '',
|
|
section_title_en VARCHAR(64) NOT NULL DEFAULT '',
|
|
section_title_zh VARCHAR(64) NOT NULL DEFAULT '',
|
|
config_json TEXT NOT NULL DEFAULT '',
|
|
sort INT NOT NULL DEFAULT 0,
|
|
status SMALLINT NOT NULL DEFAULT 0,
|
|
start_at TIMESTAMP(3),
|
|
end_at TIMESTAMP(3),
|
|
version INT NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
deleted SMALLINT NOT NULL DEFAULT 0
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_task_code ON reward_task (code) WHERE deleted = 0;
|
|
CREATE INDEX IF NOT EXISTS idx_reward_task_active
|
|
ON reward_task (status, section_code, sort, start_at, end_at) WHERE deleted = 0;
|
|
COMMENT ON TABLE reward_task IS '奖励任务配置;文案、奖励、周期、动作和上下线均由后台维护';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_task_progress (
|
|
id BIGINT PRIMARY KEY,
|
|
user_id BIGINT NOT NULL,
|
|
task_id BIGINT NOT NULL,
|
|
cycle_key VARCHAR(32) NOT NULL,
|
|
progress_value BIGINT NOT NULL DEFAULT 0,
|
|
target_value BIGINT NOT NULL DEFAULT 1,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'IN_PROGRESS',
|
|
claim_seq INT NOT NULL DEFAULT 0,
|
|
completed_at TIMESTAMP(3),
|
|
claimed_at TIMESTAMP(3),
|
|
last_event_at TIMESTAMP(3),
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_progress_cycle
|
|
ON reward_task_progress (user_id, task_id, cycle_key);
|
|
CREATE INDEX IF NOT EXISTS idx_reward_progress_user
|
|
ON reward_task_progress (user_id, status, updated_at DESC);
|
|
COMMENT ON TABLE reward_task_progress IS '用户在任务周期内的服务端进度与领取状态';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_checkin_rule_day (
|
|
id BIGINT PRIMARY KEY,
|
|
task_id BIGINT NOT NULL,
|
|
day_index INT NOT NULL,
|
|
reward_coin BIGINT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_checkin_rule_day
|
|
ON reward_checkin_rule_day (task_id, day_index);
|
|
COMMENT ON TABLE reward_checkin_rule_day IS '签到任务每日奖励规则,修改后立即由奖励页读取';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_checkin_record (
|
|
id BIGINT PRIMARY KEY,
|
|
user_id BIGINT NOT NULL,
|
|
task_id BIGINT NOT NULL,
|
|
checkin_date DATE NOT NULL,
|
|
day_index INT NOT NULL,
|
|
streak INT NOT NULL DEFAULT 1,
|
|
reward_coin BIGINT NOT NULL DEFAULT 0,
|
|
grant_id BIGINT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_checkin_date
|
|
ON reward_checkin_record (user_id, task_id, checkin_date);
|
|
CREATE INDEX IF NOT EXISTS idx_reward_checkin_user
|
|
ON reward_checkin_record (user_id, task_id, checkin_date DESC);
|
|
COMMENT ON TABLE reward_checkin_record IS '每日签到事实;用户、任务、日期唯一,防止重复签到';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_grant (
|
|
id BIGINT PRIMARY KEY,
|
|
user_id BIGINT NOT NULL,
|
|
task_id BIGINT NOT NULL,
|
|
cycle_key VARCHAR(32) NOT NULL,
|
|
claim_seq INT NOT NULL DEFAULT 1,
|
|
amount BIGINT NOT NULL,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
|
|
idempotent_key VARCHAR(64) NOT NULL,
|
|
coin_ledger_id BIGINT NOT NULL DEFAULT 0,
|
|
source VARCHAR(16) NOT NULL DEFAULT 'APP',
|
|
operator_id BIGINT NOT NULL DEFAULT 0,
|
|
failure_reason VARCHAR(255) NOT NULL DEFAULT '',
|
|
granted_at TIMESTAMP(3),
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_grant_cycle
|
|
ON reward_grant (user_id, task_id, cycle_key, claim_seq);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_grant_idem ON reward_grant (idempotent_key);
|
|
CREATE INDEX IF NOT EXISTS idx_reward_grant_user ON reward_grant (user_id, created_at DESC);
|
|
COMMENT ON TABLE reward_grant IS '奖励发放单;必须关联 coin_ledger,支持幂等与审计';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_activity (
|
|
id BIGINT PRIMARY KEY,
|
|
code VARCHAR(64) NOT NULL,
|
|
cover_url VARCHAR(512) NOT NULL DEFAULT '',
|
|
action_type VARCHAR(32) NOT NULL DEFAULT 'NONE',
|
|
action_url VARCHAR(512) NOT NULL DEFAULT '',
|
|
title_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
title_en VARCHAR(128) NOT NULL DEFAULT '',
|
|
title_zh VARCHAR(128) NOT NULL DEFAULT '',
|
|
summary_id VARCHAR(255) NOT NULL DEFAULT '',
|
|
summary_en VARCHAR(255) NOT NULL DEFAULT '',
|
|
summary_zh VARCHAR(255) NOT NULL DEFAULT '',
|
|
cta_id VARCHAR(64) NOT NULL DEFAULT '',
|
|
cta_en VARCHAR(64) NOT NULL DEFAULT '',
|
|
cta_zh VARCHAR(64) NOT NULL DEFAULT '',
|
|
sort INT NOT NULL DEFAULT 0,
|
|
status SMALLINT NOT NULL DEFAULT 0,
|
|
start_at TIMESTAMP(3),
|
|
end_at TIMESTAMP(3),
|
|
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
deleted SMALLINT NOT NULL DEFAULT 0
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_activity_code
|
|
ON reward_activity (code) WHERE deleted = 0;
|
|
CREATE INDEX IF NOT EXISTS idx_reward_activity_active
|
|
ON reward_activity (status, sort, start_at, end_at) WHERE deleted = 0;
|
|
COMMENT ON TABLE reward_activity IS '奖励中心活动卡片;一期只负责展示与跳转';
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_event_log (
|
|
id BIGINT PRIMARY KEY,
|
|
user_id BIGINT NOT NULL,
|
|
event_type VARCHAR(32) NOT NULL,
|
|
source_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
event_value BIGINT NOT NULL DEFAULT 0,
|
|
idempotent_key VARCHAR(128) NOT NULL,
|
|
occurred_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_reward_event_idem ON reward_event_log (idempotent_key);
|
|
CREATE INDEX IF NOT EXISTS idx_reward_event_user ON reward_event_log (user_id, occurred_at DESC);
|
|
COMMENT ON TABLE reward_event_log IS '服务端奖励事件去重日志,不接受客户端直接写入';
|
|
|
|
DROP TRIGGER IF EXISTS trg_reward_task_updated_at ON reward_task;
|
|
CREATE TRIGGER trg_reward_task_updated_at BEFORE UPDATE ON reward_task
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
DROP TRIGGER IF EXISTS trg_reward_task_progress_updated_at ON reward_task_progress;
|
|
CREATE TRIGGER trg_reward_task_progress_updated_at BEFORE UPDATE ON reward_task_progress
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
DROP TRIGGER IF EXISTS trg_reward_checkin_rule_day_updated_at ON reward_checkin_rule_day;
|
|
CREATE TRIGGER trg_reward_checkin_rule_day_updated_at BEFORE UPDATE ON reward_checkin_rule_day
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
DROP TRIGGER IF EXISTS trg_reward_grant_updated_at ON reward_grant;
|
|
CREATE TRIGGER trg_reward_grant_updated_at BEFORE UPDATE ON reward_grant
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
DROP TRIGGER IF EXISTS trg_reward_activity_updated_at ON reward_activity;
|
|
CREATE TRIGGER trg_reward_activity_updated_at BEFORE UPDATE ON reward_activity
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
|
|
INSERT INTO reward_task (
|
|
id, code, section_code, type, verify_mode, target_value, reward_coin, cycle_type,
|
|
action_type, title_id, title_en, title_zh, subtitle_id, subtitle_en, subtitle_zh,
|
|
cta_id, cta_en, cta_zh, section_title_id, section_title_en, section_title_zh, sort, status
|
|
) VALUES
|
|
(202611050001, 'daily_check_in', 'CHECK_IN', 'DAILY_CHECK_IN', 'SERVER', 1, 35, 'DAILY',
|
|
'NONE', 'Check-in harian', 'Daily check-in', '每日签到',
|
|
'Check-in setiap hari untuk mendapatkan koin', 'Check in every day to earn coins', '每日签到领取金币',
|
|
'Check-in', 'Check in', '签到', 'Hadiah check-in', 'Check-in rewards', '签到奖励', 10, 1),
|
|
(202611050002, 'watch_5_minutes', 'DAILY', 'WATCH_DURATION', 'SERVER', 300, 20, 'DAILY',
|
|
'OPEN_HOME', 'Tonton selama 5 menit', 'Watch for 5 minutes', '观看 5 分钟',
|
|
'Waktu menonton valid dihitung oleh server', 'Valid watch time is counted by the server', '有效观看时长由服务端统计',
|
|
'Mulai menonton', 'Watch now', '去观看', 'Tugas harian', 'Daily tasks', '每日任务', 20, 1),
|
|
(202611050003, 'watch_3_episodes', 'DAILY', 'WATCH_EPISODES', 'SERVER', 3, 25, 'DAILY',
|
|
'OPEN_HOME', 'Tonton 3 episode', 'Watch 3 episodes', '观看 3 集',
|
|
'Episode yang berbeda dihitung satu kali', 'Each distinct episode counts once', '不同分集只计一次',
|
|
'Mulai menonton', 'Watch now', '去观看', 'Tugas harian', 'Daily tasks', '每日任务', 30, 1),
|
|
(202611050004, 'complete_profile', 'NEWBIE', 'COMPLETE_PROFILE', 'SERVER', 1, 50, 'ONCE',
|
|
'OPEN_PROFILE', 'Lengkapi profil', 'Complete your profile', '完善个人资料',
|
|
'Tambahkan nama panggilan dan foto profil', 'Add a nickname and profile photo', '填写昵称并上传头像',
|
|
'Lengkapi', 'Complete', '去完成', 'Tugas pengguna baru', 'New user tasks', '新手任务', 40, 1)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
INSERT INTO reward_checkin_rule_day (id, task_id, day_index, reward_coin) VALUES
|
|
(202611050101, 202611050001, 1, 35),
|
|
(202611050102, 202611050001, 2, 35),
|
|
(202611050103, 202611050001, 3, 35),
|
|
(202611050104, 202611050001, 4, 35),
|
|
(202611050105, 202611050001, 5, 35),
|
|
(202611050106, 202611050001, 6, 55),
|
|
(202611050107, 202611050001, 7, 55)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
|
|
(1170, 'reward:list', '查看奖励任务', '订单与权益', 'BUTTON', 170),
|
|
(1171, 'reward:update', '维护奖励任务', '订单与权益', 'BUTTON', 171),
|
|
(1172, 'reward:checkin', '配置签到规则', '订单与权益', 'BUTTON', 172),
|
|
(1173, 'reward:grant', '查看与补发奖励', '订单与权益', 'BUTTON', 173)
|
|
ON CONFLICT (code) DO UPDATE SET
|
|
name = EXCLUDED.name, group_name = EXCLUDED.group_name, type = EXCLUDED.type, sort = EXCLUDED.sort;
|
|
|
|
INSERT INTO sys_role_permission (id, role_id, permission_id)
|
|
SELECT (11700000 + r.id * 100 + p.id % 100), r.id, p.id
|
|
FROM sys_role r
|
|
CROSS JOIN sys_permission p
|
|
WHERE r.code = 'super_admin' AND p.code IN ('reward:list', 'reward:update', 'reward:checkin', 'reward:grant')
|
|
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
|
|
|
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
|
|
permission_code, type, always_show, affix, sort) VALUES
|
|
(35, 22, '奖励任务', 'rewards', 'commerce-rewards',
|
|
'views/commerce/RewardTaskManagementView', '', '', 'reward:list', 'MENU', 0, 0, 3)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
parent_id = EXCLUDED.parent_id, title = EXCLUDED.title, route_path = EXCLUDED.route_path,
|
|
route_name = EXCLUDED.route_name, component = EXCLUDED.component,
|
|
permission_code = EXCLUDED.permission_code, type = EXCLUDED.type, sort = EXCLUDED.sort;
|
|
|
|
INSERT INTO sys_role_menu (id, role_id, menu_id)
|
|
SELECT (350000 + r.id), r.id, 35
|
|
FROM sys_role r
|
|
WHERE r.code = 'super_admin'
|
|
ON CONFLICT (role_id, menu_id) DO NOTHING;
|
|
|