feat(push-notifications): 新增挑战提醒定时推送功能

新增每日定时推送系统,根据用户参与状态发送不同类型的挑战提醒:
- 已参与用户:每日发送鼓励推送
- 未参与用户:隔天发送挑战邀请
- 匿名用户:隔天发送通用邀请

包含推送历史记录表、定时任务调度、多类型文案模板和防重复发送机制
This commit is contained in:
richarjiang
2025-11-03 17:49:14 +08:00
parent 3a3939e1ba
commit 37cc2a729b
9 changed files with 710 additions and 26 deletions

View File

@@ -0,0 +1,83 @@
import { Table, Column, Model, DataType, Index } from 'sequelize-typescript';
export enum ReminderType {
CHALLENGE_ENCOURAGEMENT = 'challenge_encouragement', // 已参与挑战用户的鼓励推送
CHALLENGE_INVITATION = 'challenge_invitation', // 未参与挑战用户的邀请推送
GENERAL_INVITATION = 'general_invitation', // 无userId用户的通用邀请
}
@Table({
tableName: 't_push_reminder_history',
underscored: true,
})
export class PushReminderHistory extends Model {
@Column({
type: DataType.UUID,
defaultValue: DataType.UUIDV4,
primaryKey: true,
})
declare id: string;
@Column({
type: DataType.STRING,
allowNull: true,
comment: '用户ID可能为空',
})
declare userId: string | null;
@Column({
type: DataType.STRING,
allowNull: false,
comment: '设备推送令牌',
})
declare deviceToken: string;
@Column({
type: DataType.ENUM(...Object.values(ReminderType)),
allowNull: false,
comment: '提醒类型',
})
declare reminderType: ReminderType;
@Column({
type: DataType.DATE,
allowNull: false,
comment: '最后发送时间',
})
declare lastSentAt: Date;
@Column({
type: DataType.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '发送次数',
})
declare sentCount: number;
@Column({
type: DataType.DATE,
allowNull: true,
comment: '下次可发送时间',
})
declare nextAvailableAt: Date | null;
@Column({
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否激活',
})
declare isActive: boolean;
@Column({
type: DataType.DATE,
defaultValue: DataType.NOW,
})
declare createdAt: Date;
@Column({
type: DataType.DATE,
defaultValue: DataType.NOW,
})
declare updatedAt: Date;
}