Files
danyang-match/miniapp/stores/activityStore.js
T

186 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ActivityStore(规范 §131)
*
* 职责:
* - 维护活动列表(首页 + 我的活动)
* - 维护活动报名状态 / 互选意向(AGENTS 第 6 条:单向意向保密)
* - 维护我报名的活动集合,用于徽标与"我的活动"页
*
* 不变量(AGENTS.md):
* - 第 1 条:用户永久免费——活动 feeType=paid 字段仅作展示,前端不实现任何支付入口。
* - 第 6 条:活动单向意向保密——submitInterests 返回的 matchedUserIds 仅含双向匹配,
* 未匹配的人不在前端可见;Store 不缓存"谁单独选了我"的字段。
*/
const BaseStore = require('./baseStore');
const { activityService } = require('../services');
const { ACTIVITY_STATUS, REGISTRATION_STATUS } = require('../constants');
class ActivityStore extends BaseStore {
constructor() {
super({
/** @type {import('../types').Activity[]} */
activities: [],
activitiesCursor: null,
activitiesHasMore: true,
activitiesLoading: false,
/** @type {import('../types').Activity[]} */
myActivities: [],
myActivitiesCursor: null,
myActivitiesHasMore: true,
myActivitiesLoading: false,
/** @type {Record<string, import('../types').Activity>} 活动详情缓存 */
detailCache: {},
/** @type {Record<string, import('../types').RegistrationStatus>} activityId -> 报名状态 */
registrationStatusByActivity: {},
/** 我报名成功且未结束的活动数(徽标用) */
myActiveCount: 0,
lastError: null,
});
}
/** 拉取活动列表(首页) */
async fetchActivities(params = {}) {
if (this.state.activitiesLoading) return;
this.setState({ activitiesLoading: true, lastError: null });
try {
const result = await activityService.getActivities({
status: ACTIVITY_STATUS.REGISTRATION_OPEN,
...params,
});
this.setState({
activities: result.items || [],
activitiesCursor: result.nextCursor || null,
activitiesHasMore: !!result.nextCursor,
activitiesLoading: false,
});
return result;
} catch (err) {
this.setState({ activitiesLoading: false, lastError: err });
throw err;
}
}
/** 加载更多活动 */
async loadMoreActivities() {
if (this.state.activitiesLoading || !this.state.activitiesCursor) return;
this.setState({ activitiesLoading: true });
try {
const result = await activityService.getActivities({
cursor: this.state.activitiesCursor,
pageSize: 20,
});
this.setState({
activities: [...this.state.activities, ...(result.items || [])],
activitiesCursor: result.nextCursor || null,
activitiesHasMore: !!result.nextCursor,
activitiesLoading: false,
});
} catch (err) {
this.setState({ activitiesLoading: false, lastError: err });
throw err;
}
}
/** 拉取我的活动 */
async fetchMyActivities(params = {}) {
if (this.state.myActivitiesLoading) return;
this.setState({ myActivitiesLoading: true, lastError: null });
try {
const result = await activityService.getMyActivities(params);
const items = result.items || [];
this.setState({
myActivities: items,
myActivitiesCursor: result.nextCursor || null,
myActivitiesHasMore: !!result.nextCursor,
myActivitiesLoading: false,
myActiveCount: items.filter(
(a) =>
a.registrationStatus === REGISTRATION_STATUS.APPROVED &&
a.status !== ACTIVITY_STATUS.ENDED &&
a.status !== ACTIVITY_STATUS.CANCELLED,
).length,
});
return result;
} catch (err) {
this.setState({ myActivitiesLoading: false, lastError: err });
throw err;
}
}
/** 加载更多我的活动 */
async loadMoreMyActivities() {
if (this.state.myActivitiesLoading || !this.state.myActivitiesCursor) return;
this.setState({ myActivitiesLoading: true });
try {
const result = await activityService.getMyActivities({
cursor: this.state.myActivitiesCursor,
pageSize: 20,
});
this.setState({
myActivities: [...this.state.myActivities, ...(result.items || [])],
myActivitiesCursor: result.nextCursor || null,
myActivitiesHasMore: !!result.nextCursor,
myActivitiesLoading: false,
});
} catch (err) {
this.setState({ myActivitiesLoading: false, lastError: err });
throw err;
}
}
/** 拉取活动详情(优先取缓存) */
async fetchActivityDetail(activityId, { force = false } = {}) {
if (!force && this.state.detailCache[activityId]) {
return this.state.detailCache[activityId];
}
const detail = await activityService.getActivityDetail(activityId);
this.setState({
detailCache: { ...this.state.detailCache, [activityId]: detail },
registrationStatusByActivity: {
...this.state.registrationStatusByActivity,
[activityId]: detail.registrationStatus,
},
});
return detail;
}
/**
* 报名活动
* @param {string} activityId
* @param {{ remark?: string }} [payload]
*/
async register(activityId, payload) {
const result = await activityService.register(activityId, payload);
this.setState({
registrationStatusByActivity: {
...this.state.registrationStatusByActivity,
[activityId]: REGISTRATION_STATUS.PENDING,
},
});
return result;
}
/**
* 提交活动互选意向(AGENTS 第 6 条:单向意向保密)
* @param {string} activityId
* @param {string[]} likedUserIds
* @returns {Promise<{ matchedUserIds: string[] }>} 仅返回双向匹配的 userIds
*/
async submitInterests(activityId, likedUserIds) {
return activityService.submitInterests(activityId, { likedUserIds });
}
/** 提交活动反馈 */
async submitFeedback(activityId, payload) {
return activityService.submitFeedback(activityId, payload);
}
}
const activityStore = new ActivityStore();
module.exports = activityStore;