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,127 @@
import { ChallengeType } from '../../challenges/models/challenge.model';
/**
* 挑战鼓励文案模板 - 针对已参与挑战的用户
*/
export const ENCOURAGEMENT_TEMPLATES = {
[ChallengeType.WATER]: [
{ title: '饮水挑战进行中', body: '今天已有多人完成饮水目标!你也要记得多喝水哦!' },
{ title: '健康饮水提醒', body: '加入饮水挑战的小伙伴们今天都很棒!你今天喝水达标了吗?' },
{ title: '水分补充时间', body: '挑战者们都在坚持每日饮水目标,你也要跟上大家的步伐!' },
{ title: '饮水习惯养成', body: '看到很多挑战伙伴都养成了良好饮水习惯,你也是其中一员!' },
{ title: '团队饮水挑战', body: '今天挑战群里又有很多人完成了目标!别掉队,一起加油!' },
],
[ChallengeType.EXERCISE]: [
{ title: '运动挑战进行时', body: '今天已有多人完成运动目标!你也来动一动吧!' },
{ title: '活力运动提醒', body: '挑战伙伴们都在坚持运动,每一滴汗水都是进步的见证!' },
{ title: '运动习惯养成', body: '看到很多挑战者都养成了运动习惯,你也是其中一员!' },
{ title: '团队运动挑战', body: '今天运动挑战群里又有很多人完成了目标!别掉队,一起加油!' },
{ title: '运动生活分享', body: '挑战者们都在分享运动心得,你的今天运动了吗?' },
],
[ChallengeType.DIET]: [
{ title: '饮食挑战进行中', body: '今天已有多人记录了健康饮食!你也要记得合理搭配哦!' },
{ title: '营养均衡提醒', body: '挑战伙伴们都在坚持健康饮食,为身体提供充足营养!' },
{ title: '健康饮食分享', body: '看到很多挑战者都在分享健康餐,你今天吃了什么?' },
{ title: '团队饮食挑战', body: '今天饮食挑战群里又有很多人完成了目标!一起健康饮食!' },
{ title: '营养生活记录', body: '挑战者们都在记录饮食,关注健康,让每一餐都有意义!' },
],
[ChallengeType.MOOD]: [
{ title: '心情记录挑战', body: '今天已有多人记录了心情!你也来分享今天的感受吧!' },
{ title: '情绪管理提醒', body: '挑战伙伴们都在关注情绪变化,让心情更加愉悦平和!' },
{ title: '心情日记分享', body: '看到很多挑战者都在记录心情故事,你今天心情如何?' },
{ title: '团队心情挑战', body: '今天心情挑战群里又有很多人分享了感受!一起关注心理健康!' },
{ title: '情绪健康关怀', body: '挑战者们都在关爱自己的情绪,从记录心情开始!' },
],
[ChallengeType.SLEEP]: [
{ title: '睡眠挑战进行中', body: '今天已有多人保持了规律作息!你也要早睡早起哦!' },
{ title: '优质睡眠提醒', body: '挑战伙伴们都在保持良好睡眠质量,享受每一个宁静的夜晚!' },
{ title: '规律作息养成', body: '看到很多挑战者都养成了规律作息,你也是其中一员!' },
{ title: '团队睡眠挑战', body: '今天睡眠挑战群里又有很多人完成了目标!一起健康作息!' },
{ title: '休息时光分享', body: '挑战者们都在分享睡眠心得,你今天休息得好吗?' },
],
[ChallengeType.WEIGHT]: [
{ title: '体重管理挑战', body: '今天已有多人记录了体重变化!你也要坚持健康生活方式!' },
{ title: '健康体重提醒', body: '挑战伙伴们都在坚持体重管理,享受轻松自在的生活!' },
{ title: '体重记录分享', body: '看到很多挑战者都在记录体重变化,见证自己的进步!' },
{ title: '团队体重挑战', body: '今天体重挑战群里又有很多人完成了目标!一起健康生活!' },
{ title: '健康生活记录', body: '挑战者们都在坚持健康饮食+适量运动,你也是其中一员!' },
],
};
/**
* 挑战邀请文案模板 - 针对未参与挑战但有userId的用户
*/
export const INVITATION_TEMPLATES = {
[ChallengeType.WATER]: [
{ title: '饮水挑战邀请', body: '加入21天饮水挑战养成健康饮水习惯' },
{ title: '健康饮水挑战', body: '每天适量饮水,让身体更健康。立即加入挑战!' },
{ title: '饮水习惯养成', body: '挑战自己,养成良好饮水习惯,收获健康!' },
],
[ChallengeType.EXERCISE]: [
{ title: '运动挑战邀请', body: '加入运动挑战,让身体充满活力!' },
{ title: '健身挑战', body: '21天运动挑战塑造更好的自己' },
{ title: '运动习惯养成', body: '坚持运动,收获健康体魄,立即加入!' },
],
[ChallengeType.DIET]: [
{ title: '饮食挑战邀请', body: '加入健康饮食挑战,培养良好饮食习惯!' },
{ title: '营养挑战', body: '21天饮食挑战让营养均衡成为习惯' },
{ title: '健康饮食计划', body: '科学饮食,健康生活,从挑战开始!' },
],
[ChallengeType.MOOD]: [
{ title: '心情记录挑战', body: '加入心情记录挑战,关注情绪健康!' },
{ title: '情绪管理挑战', body: '21天心情记录让心灵更加宁静。' },
{ title: '心理健康挑战', body: '记录心情,关爱自己,从现在开始!' },
],
[ChallengeType.SLEEP]: [
{ title: '睡眠挑战邀请', body: '加入优质睡眠挑战,养成规律作息!' },
{ title: '作息规律挑战', body: '21天睡眠挑战让身体得到充分休息。' },
{ title: '健康睡眠计划', body: '改善睡眠质量,享受每一个清晨!' },
],
[ChallengeType.WEIGHT]: [
{ title: '体重管理挑战', body: '加入体重管理挑战,收获健康理想体重!' },
{ title: '健康体重挑战', body: '21天体重管理见证自己的变化' },
{ title: '体重目标挑战', body: '科学管理体重,享受健康生活!' },
],
};
/**
* 通用邀请文案模板 - 针对没有userId的用户
*/
export const GENERAL_INVITATION_TEMPLATES = [
{ title: '健康挑战邀请', body: '加入我们的健康挑战,开启健康生活新篇章!' },
{ title: '21天挑战', body: '21天养成健康习惯你准备好了吗' },
{ title: '健康生活', body: '追求健康生活,从参加挑战开始!' },
{ title: '挑战自我', body: '挑战自己,收获健康,让生活更美好!' },
{ title: '健康之旅', body: '开启健康之旅,遇见更好的自己!' },
];
/**
* 随机选择一个模板
*/
export function getRandomTemplate<T>(templates: T[]): T {
const randomIndex = Math.floor(Math.random() * templates.length);
return templates[randomIndex];
}
/**
* 根据挑战类型获取鼓励文案
*/
export function getEncouragementTemplate(challengeType: ChallengeType) {
const templates = ENCOURAGEMENT_TEMPLATES[challengeType] || ENCOURAGEMENT_TEMPLATES[ChallengeType.EXERCISE];
return getRandomTemplate(templates);
}
/**
* 根据挑战类型获取邀请文案
*/
export function getInvitationTemplate(challengeType: ChallengeType) {
const templates = INVITATION_TEMPLATES[challengeType] || INVITATION_TEMPLATES[ChallengeType.EXERCISE];
return getRandomTemplate(templates);
}
/**
* 获取通用邀请文案
*/
export function getGeneralInvitationTemplate() {
return getRandomTemplate(GENERAL_INVITATION_TEMPLATES);
}