| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- import { Injectable } from '@angular/core';
- import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
- import { catchError, map, Observable, of } from 'rxjs';
- // 定义模板类型枚举
- export enum TemplateType {
- OTHER = 0,
- NEW_USER_WELCOM = 1, // 欢迎与引导
- ENCOURAGE_AND_REWARD = 2, // 鼓励与奖励
- DAILY_REWARD_REMINDER = 3, // 每日奖励提醒
- PERSONALIZED_RECOMMENDATION = 4, // 个性化推荐
- NEW_CONTENT_REMINDER = 5, // 新画作提醒
- HOT_CONTENT_RECOMMENDATION = 6, // 热门画作推荐
- LIMITED_TIME_EVENT_REMINDER = 7, // 限时活动提醒
- HOLIDAY_CELEBRATION = 8, // 节日/特殊日庆贺
- COMPLETION_ENCOURAGEMENT = 9, // 完成度鼓励
- SOCIAL_SHARING_ENCOURAGEMENT = 10, // 社交分享鼓励
- UNACTIVE_USER_RECALL = 11, // 长时间不活跃用户召回
- NEW_FEATURE_INTRODUCTION = 12, // 新功能介绍
- CHALLENGE_TASK_REMINDER = 13, // 挑战任务提醒
- }
- // 定义模板类型映射
- export const TEMPLATE_TYPE_MAP: Record<TemplateType, string> = {
- [TemplateType.OTHER]: '其他',
- [TemplateType.NEW_USER_WELCOM]: '欢迎与引导',
- [TemplateType.ENCOURAGE_AND_REWARD]: '鼓励与奖励',
- [TemplateType.DAILY_REWARD_REMINDER]: '每日奖励提醒',
- [TemplateType.PERSONALIZED_RECOMMENDATION]: '个性化推荐',
- [TemplateType.NEW_CONTENT_REMINDER]: '新画作提醒',
- [TemplateType.HOT_CONTENT_RECOMMENDATION]: '热门画作推荐',
- [TemplateType.LIMITED_TIME_EVENT_REMINDER]: '限时活动提醒',
- [TemplateType.HOLIDAY_CELEBRATION]: '假日/节日主题',
- [TemplateType.COMPLETION_ENCOURAGEMENT]: '完成度鼓励',
- [TemplateType.SOCIAL_SHARING_ENCOURAGEMENT]: '社交分享鼓励',
- [TemplateType.UNACTIVE_USER_RECALL]: '长时间不活跃用户召回',
- [TemplateType.NEW_FEATURE_INTRODUCTION]: '新功能介绍',
- [TemplateType.CHALLENGE_TASK_REMINDER]: '挑战任务提醒',
- };
- // 新增 ActionType 枚举和映射
- export enum ActionType {
- GO_APP = 'go/app',
- GO_ART = 'go/art',
- GO_ALBUM = 'go/album',
- GAIN_HINT = 'gain/hint',
- KEEP_GOING = 'keepgoing',
- }
- export const ACTION_TYPE_MAP: Record<ActionType, string> = {
- [ActionType.GO_APP]: '打开应用',
- [ActionType.GO_ART]: '打开填色作品',
- [ActionType.GO_ALBUM]: '打开专辑',
- [ActionType.GAIN_HINT]: '获取提示奖励',
- [ActionType.KEEP_GOING]: '继续完成作品',
- };
- export const ACTION_PARAM_PLACEHOLDERS: Record<ActionType, string> = {
- [ActionType.GO_APP]: '',
- [ActionType.GO_ART]: '输入作品ID',
- [ActionType.GO_ALBUM]: '输入专辑ID',
- [ActionType.GAIN_HINT]: '输入提示数量',
- [ActionType.KEEP_GOING]: '输入作品ID',
- };
- // 定义消息模板接口
- export interface IMessageTemplate {
- _id: string;
- templateName: string;
- templateType: TemplateType;
- description: string;
- messageTitle: { [key: string]: string };
- messageContent: { [key: string]: string };
- image?: string;
- bigger?: boolean;
- action?: ActionType;
- param?: string;
- extend?: string;
- createdAt: Date;
- updatedAt: Date;
- }
- // 定义创建/更新模板的数据结构
- export interface MessageTemplateData {
- templateName: string;
- templateType: TemplateType;
- descritpion: string;
- messageTitle: { [key: string]: string };
- messageContent: { [key: string]: string };
- image?: string;
- bigger?: boolean;
- action?: string;
- param?: string;
- extend?: string;
- }
- // 定义目标用户筛选项相关接口
- export interface FilterCondition {
- field: string;
- operator: string;
- value: any;
- }
- export interface FieldConfig {
- value: string;
- label: string;
- type: 'country' | 'date' | 'number' | 'number_days' | 'text';
- }
- export interface OperatorOption {
- value: string;
- label: string;
- valueType: 'single' | 'multiple' | 'range';
- }
- // 更新模板信息接口
- export interface IMessageTemplateInfo {
- _id: string;
- templateName: string;
- templateType?: TemplateType;
- description?: string;
- createdAt?: Date;
- updatedAt?: Date;
- }
- // 策略接口保持不变
- export interface IMessageStrategy {
- _id: string;
- name: string;
- description?: string;
- templates: IMessageTemplateInfo[]; // 使用 IMessageTemplateInfo 类型
- createdAt: Date;
- updatedAt: Date;
- }
- // 创建策略数据接口保持不变
- export interface MessageStrategyData {
- name: string;
- description?: string;
- templates: string[]; // 创建时只需要模板ID数组
- }
- // 定义消息活动接口
- export interface IMessageActivity {
- _id: string;
- name: string;
- templateId: string;
- image?: string;
- bigger: boolean;
- action?: ActionType;
- description: string;
- param?: string;
- extend?: string;
- strategy: number;
- filter?: FilterCondition[];
- scheduleAt?: string;
- everyday: boolean;
- status: number;
- createdAt: Date;
- updatedAt: Date;
- }
- // 定义创建/更新消息活动的数据结构
- export interface MessageActivityData {
- name: string;
- templateId: string;
- image?: string;
- bigger: boolean;
- action?: string;
- param?: string;
- extend?: string;
- strategy: number;
- filter?: FilterCondition[];
- scheduleAt?: string;
- everyday: boolean;
- status: number;
- description: string;
- }
- // 定义消息记录接口
- export interface IMessageRecord {
- _id: string;
- uid: string;
- cc?: string;
- activityId?: string;
- activityName?: string;
- templateId?: string;
- templateName?: string;
- strategyId?: string;
- strategyName?: string;
- title: string;
- content: string;
- image?: string;
- bigger: boolean;
- action?: string;
- param?: string;
- extend?: string;
- status: number;
- inforeground?: boolean;
- errno?: string;
- fcmReceipt?: string;
- plannedSendAt?: Date;
- actualSendAt?: Date;
- deliveredAt?: Date;
- openedAt?: Date;
- createdAt: Date;
- updatedAt: Date;
- }
- // 定义分页响应接口
- export interface IMessageRecordPaginationResponse {
- success: boolean;
- data: IMessageRecord[];
- pagination: {
- total: number;
- page: number;
- limit: number;
- totalPages: number;
- };
- }
- // 定义删除操作的响应结构
- interface DeleteResponse {
- deletedCount?: number;
- }
- @Injectable({
- providedIn: 'root',
- })
- export class MessageService {
- private templatesApiUrl = '/api/message-template';
- private activitiesApiUrl = '/api/message-activity';
- private activityListApiUri = '/api/message-activities';
- private recordsApiUrl = '/api/message-record';
- private recordsListApiUrl = '/api/message-records';
- private strategiesApiUrl = '/api/message-strategy';
- private httpOptions = {
- headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
- };
- constructor(private http: HttpClient) {}
- /**
- * 获取所有客户端行为选项
- */
- getActionOptions(): { value: ActionType; label: string }[] {
- return Object.entries(ACTION_TYPE_MAP).map(([value, label]) => ({
- value: value as ActionType,
- label,
- }));
- }
- /**
- * 获取行为参数占位符
- */
- getActionParamPlaceholder(action: ActionType): string {
- return ACTION_PARAM_PLACEHOLDERS[action] || '输入参数';
- }
- /**
- * 获取所有模板类型选项
- */
- getTemplateTypeOptions(): { value: TemplateType; label: string }[] {
- return Object.entries(TEMPLATE_TYPE_MAP).map(([value, label]) => ({
- value: parseInt(value, 10) as TemplateType,
- label,
- }));
- }
- /**
- * 获取模板类型映射
- */
- getTemplateTypeMap(): Record<TemplateType, string> {
- return TEMPLATE_TYPE_MAP;
- }
- /**
- * 模板管理 API
- */
- createTemplate(
- templateData: MessageTemplateData
- ): Observable<IMessageTemplate> {
- return this.http.post<IMessageTemplate>(
- this.templatesApiUrl,
- templateData,
- this.httpOptions
- );
- }
- getTemplateById(templateId: string): Observable<IMessageTemplate | null> {
- return this.http.get<IMessageTemplate | null>(
- `${this.templatesApiUrl}/${templateId}`,
- this.httpOptions
- );
- }
- getTemplateByName(templateName: string): Observable<IMessageTemplate | null> {
- return this.http.get<IMessageTemplate | null>(
- `${this.templatesApiUrl}/name/${templateName}`,
- this.httpOptions
- );
- }
- getAllTemplates(): Observable<IMessageTemplate[]> {
- return this.http.get<IMessageTemplate[]>(
- this.templatesApiUrl,
- this.httpOptions
- );
- }
- updateTemplate(
- templateId: string,
- updateData: Partial<MessageTemplateData>
- ): Observable<IMessageTemplate | null> {
- return this.http.put<IMessageTemplate | null>(
- `${this.templatesApiUrl}/${templateId}`,
- updateData,
- this.httpOptions
- );
- }
- deleteTemplate(templateId: string): Observable<DeleteResponse> {
- return this.http.delete<DeleteResponse>(
- `${this.templatesApiUrl}/${templateId}`,
- this.httpOptions
- );
- }
- getAllStrategies(): Observable<IMessageStrategy[]> {
- return this.http
- .get<IMessageStrategy[]>('/api/message-strategies', this.httpOptions)
- .pipe(
- map((response) => (Array.isArray(response) ? response : [])),
- catchError((err) => {
- console.error('获取策略失败:', err);
- return of([]);
- })
- );
- }
- getStrategyById(id: string): Observable<IMessageStrategy | null> {
- return this.http.get<IMessageStrategy | null>(
- `${this.strategiesApiUrl}/${id}`,
- this.httpOptions
- );
- }
- createStrategy(
- strategyData: MessageStrategyData
- ): Observable<IMessageStrategy> {
- return this.http.post<IMessageStrategy>(
- this.strategiesApiUrl,
- strategyData,
- this.httpOptions
- );
- }
- updateStrategy(
- id: string,
- updateData: Partial<MessageStrategyData>
- ): Observable<IMessageStrategy | null> {
- return this.http.put<IMessageStrategy | null>(
- `${this.strategiesApiUrl}/${id}`,
- updateData,
- this.httpOptions
- );
- }
- deleteStrategy(id: string): Observable<{ deletedCount?: number }> {
- return this.http.delete<{ deletedCount?: number }>(
- `${this.strategiesApiUrl}/${id}`,
- this.httpOptions
- );
- }
- /**
- * 活动管理 API
- */
- getAllActivities(): Observable<IMessageActivity[]> {
- return this.http.get<IMessageActivity[]>(
- this.activityListApiUri,
- this.httpOptions
- );
- }
- getActivityById(activityId: string): Observable<IMessageActivity | null> {
- return this.http.get<IMessageActivity | null>(
- `${this.activitiesApiUrl}/${activityId}`,
- this.httpOptions
- );
- }
- createActivity(
- activityData: MessageActivityData
- ): Observable<IMessageActivity> {
- return this.http.post<IMessageActivity>(
- this.activitiesApiUrl,
- activityData,
- this.httpOptions
- );
- }
- updateActivity(
- id: string,
- updateData: Partial<MessageActivityData>
- ): Observable<IMessageActivity | null> {
- return this.http.put<IMessageActivity | null>(
- `${this.activitiesApiUrl}/${id}`,
- updateData,
- this.httpOptions
- );
- }
- deleteActivity(id: string): Observable<DeleteResponse> {
- return this.http.delete<DeleteResponse>(
- `${this.activitiesApiUrl}/${id}`,
- this.httpOptions
- );
- }
- updateActivityStatus(
- id: string,
- status: number
- ): Observable<IMessageActivity | null> {
- return this.http.put<IMessageActivity | null>(
- `${this.activitiesApiUrl}/${id}/status`,
- { status },
- this.httpOptions
- );
- }
- /**
- * 语言支持相关
- */
- getSupportedLanguages(): { code: string; name: string }[] {
- return [
- { code: 'en', name: 'English' },
- { code: 'zh-cn', name: '简体中文' },
- { code: 'zh-tw', name: '繁體中文' },
- { code: 'es', name: 'Español' },
- { code: 'fr', name: 'Français' },
- { code: 'de', name: 'Deutsch' },
- { code: 'ja', name: '日本語' },
- { code: 'ko', name: '한국어' },
- { code: 'ru', name: 'Русский' },
- { code: 'pt', name: 'Português' },
- ];
- }
- getLanguageName(code: string): string {
- const lang = this.getSupportedLanguages().find((l) => l.code === code);
- return lang ? lang.name : code;
- }
- /**
- * 目标用户筛选相关
- */
- countTargetUsers(filters: FilterCondition[]): Observable<number> {
- return this.http.post<number>(
- `/api/users/count`,
- { filters },
- this.httpOptions
- );
- }
- /**
- * 模板内容验证
- */
- validateTemplateContent(
- content: string
- ): Observable<{ valid: boolean; message?: string }> {
- return this.http.post<{ valid: boolean; message?: string }>(
- `${this.templatesApiUrl}/validate`,
- { content },
- this.httpOptions
- );
- }
- /**
- * 模板名称可用性检查
- */
- checkTemplateNameAvailable(name: string): Observable<{ available: boolean }> {
- return this.http.post<{ available: boolean }>(
- `${this.templatesApiUrl}/check-name`,
- { name },
- this.httpOptions
- );
- }
- /**
- * 获取模板默认内容
- */
- getDefaultTemplateContent(
- language: string
- ): Observable<{ title: string; content: string }> {
- return this.http.get<{ title: string; content: string }>(
- `${this.templatesApiUrl}/default-content/${language}`,
- this.httpOptions
- );
- }
- /**
- * 消息记录管理 API
- */
- createRecord(
- recordData: Partial<IMessageRecord>
- ): Observable<IMessageRecord> {
- return this.http.post<IMessageRecord>(
- this.recordsApiUrl,
- recordData,
- this.httpOptions
- );
- }
- getPaginatedRecords(params: {
- page?: number;
- limit?: number;
- uid?: string;
- activityId?: string;
- status?: number;
- startDate?: string;
- endDate?: string;
- }): Observable<IMessageRecordPaginationResponse> {
- let httpParams = new HttpParams();
- for (const key in params) {
- if (params.hasOwnProperty(key) && params[key as keyof typeof params]) {
- httpParams = httpParams.append(
- key,
- String(params[key as keyof typeof params])
- );
- }
- }
- return this.http.get<IMessageRecordPaginationResponse>(
- this.recordsListApiUrl,
- {
- ...this.httpOptions,
- params: httpParams,
- }
- );
- }
- getRecordById(recordId: string): Observable<IMessageRecord> {
- return this.http.get<IMessageRecord>(
- `${this.recordsApiUrl}/${recordId}`,
- this.httpOptions
- );
- }
- updateRecord(
- recordId: string,
- updateData: Partial<IMessageRecord>
- ): Observable<IMessageRecord> {
- return this.http.put<IMessageRecord>(
- `${this.recordsApiUrl}/${recordId}`,
- updateData,
- this.httpOptions
- );
- }
- }
|