- 数据库新增 type 列区分 water/exercise/diet/mood/sleep/weight 六类挑战 - 进度上报由增量模式改为绝对值模式,字段 increment_value → reportedValue - 服务层按 challenge.targetValue 判断当日是否完成,再按 minimumCheckInDays 统计总进度 - 相关 DTO 与模型同步更新,支持新类型返回 BREAKING CHANGE: 上报接口字段由 increment 改为 value,且为当日绝对值
150 lines
3.2 KiB
TypeScript
150 lines
3.2 KiB
TypeScript
import { Table, Column, DataType, Model, HasMany } from 'sequelize-typescript';
|
||
import { ChallengeParticipant } from './challenge-participant.model';
|
||
import { col } from 'sequelize';
|
||
|
||
export enum ChallengeStatus {
|
||
UPCOMING = 'upcoming',
|
||
ONGOING = 'ongoing',
|
||
EXPIRED = 'expired',
|
||
}
|
||
|
||
export enum ChallengeType {
|
||
WATER = 'water',
|
||
EXERCISE = 'exercise',
|
||
DIET = 'diet',
|
||
MOOD = 'mood',
|
||
SLEEP = 'sleep',
|
||
WEIGHT = 'weight',
|
||
}
|
||
|
||
@Table({
|
||
tableName: 't_challenges',
|
||
underscored: true,
|
||
})
|
||
export class Challenge extends Model {
|
||
@Column({
|
||
type: DataType.CHAR(36),
|
||
defaultValue: DataType.UUIDV4,
|
||
primaryKey: true,
|
||
})
|
||
declare id: string;
|
||
|
||
@Column({
|
||
type: DataType.STRING(255),
|
||
allowNull: false,
|
||
comment: '挑战标题',
|
||
})
|
||
declare title: string;
|
||
|
||
@Column({
|
||
type: DataType.STRING(512),
|
||
allowNull: true,
|
||
comment: '挑战封面图',
|
||
})
|
||
declare image: string;
|
||
|
||
@Column({
|
||
type: DataType.BIGINT,
|
||
allowNull: false,
|
||
comment: '挑战开始时间(时间戳)',
|
||
})
|
||
declare startAt: number;
|
||
|
||
@Column({
|
||
type: DataType.BIGINT,
|
||
allowNull: false,
|
||
comment: '挑战结束时间(时间戳)',
|
||
})
|
||
declare endAt: number;
|
||
|
||
@Column({
|
||
type: DataType.STRING(128),
|
||
allowNull: true,
|
||
comment: '周期标签,例如「21天挑战」',
|
||
})
|
||
declare periodLabel: string | null;
|
||
|
||
@Column({
|
||
type: DataType.STRING(128),
|
||
allowNull: false,
|
||
comment: '持续时间标签,例如「持续21天」',
|
||
})
|
||
declare durationLabel: string;
|
||
|
||
@Column({
|
||
type: DataType.STRING(255),
|
||
allowNull: false,
|
||
comment: '挑战要求标签,例如「每日练习 1 次」',
|
||
})
|
||
declare requirementLabel: string;
|
||
|
||
@Column({
|
||
type: DataType.TEXT,
|
||
allowNull: true,
|
||
comment: '挑战概要说明',
|
||
})
|
||
declare summary: string | null;
|
||
|
||
@Column({
|
||
type: DataType.INTEGER,
|
||
allowNull: false,
|
||
comment: '挑战目标值(例如需要完成的天数)',
|
||
})
|
||
declare targetValue: number;
|
||
|
||
@Column({
|
||
type: DataType.STRING(64),
|
||
allowNull: false,
|
||
defaultValue: '天',
|
||
comment: '进度单位,用于展示排行榜指标',
|
||
})
|
||
declare progressUnit: string;
|
||
|
||
@Column({
|
||
type: DataType.INTEGER,
|
||
allowNull: false,
|
||
defaultValue: 0,
|
||
comment: '最低打卡天数,用于判断挑战成功',
|
||
})
|
||
declare minimumCheckInDays: number;
|
||
|
||
@Column({
|
||
type: DataType.STRING(255),
|
||
allowNull: true,
|
||
comment: '排行榜描述,例如「连续打卡榜」',
|
||
})
|
||
declare rankingDescription: string | null;
|
||
|
||
@Column({
|
||
type: DataType.STRING(255),
|
||
allowNull: false,
|
||
comment: '高亮标题',
|
||
})
|
||
declare highlightTitle: string;
|
||
|
||
@Column({
|
||
type: DataType.STRING(255),
|
||
allowNull: false,
|
||
comment: '高亮副标题',
|
||
})
|
||
declare highlightSubtitle: string;
|
||
|
||
@Column({
|
||
type: DataType.STRING(128),
|
||
allowNull: false,
|
||
comment: 'CTA 按钮文字',
|
||
})
|
||
declare ctaLabel: string;
|
||
|
||
@Column({
|
||
type: DataType.ENUM('water', 'exercise', 'diet', 'mood', 'sleep', 'weight'),
|
||
allowNull: false,
|
||
defaultValue: ChallengeType.WATER,
|
||
comment: '挑战类型',
|
||
})
|
||
declare type: ChallengeType;
|
||
|
||
@HasMany(() => ChallengeParticipant)
|
||
declare participants?: ChallengeParticipant[];
|
||
}
|