Files
danyang-match/miniapp/pages/profile-detail/profile-detail.js
T

243 lines
8.1 KiB
JavaScript
Raw Normal View History

2026-09-09 22:05:00 +08:00
/**
* 用户资料详情页 U21
*
* 重构要点(遵循 AGENTS.md 不变量):
* - 数据获取:recommendationService.getUserDetail(API_INVENTORY 行 43),不再直连 api 模块
* - 喜欢:matchStore.like(mutualMatch=true 时跳转会话页,AGENTS 第 3/6 条)
* - 拉黑:blockUser 跨 Store 编排(MatchStore + MessageStore 同步停用,AGENTS 第 4 条)
* - 举报:跳转 report 页,不在本页处理
* - 敏感数据:profile.birthday/身份证/手机号 仅服务端持有,前端不缓存(AGENTS 第 8 条)
* - Required 不可放宽:本页只读展示,无任何放宽择偶条件的入口(AGENTS 第 2 条)
*/
const { matchStore, appStore, blockUser } = require('../../stores');
const { recommendationService } = require('../../services');
const { to, DANGER_ACTION, RELATION_STATUS, BUSINESS_ERROR_CODE } = require('../../constants');
Page({
data: {
userId: '',
profile: null,
loading: true,
loadError: '',
/** @type {import('../../types').RelationStatus} */
relationStatus: RELATION_STATUS.NEUTRAL,
isLiked: false,
isMutual: false,
canChat: false,
actionLoading: false,
statusBarHeight: 20,
navBarHeight: 44,
showBlockDialog: false,
blockAction: DANGER_ACTION.BLOCK,
/** 派生的展示字段 */
displayName: '',
ageText: '',
metaLine: '',
badges: [],
basicInfo: [],
attitudeInfo: [],
},
onLoad(options) {
const userId = options.id || '';
if (!userId) {
this.setData({ loading: false, loadError: '用户不存在' });
return;
}
// 状态栏高度由 app.js 注入到全局,这里读一次
try {
const sysInfo = wx.getSystemInfoSync ? wx.getSystemInfoSync() : null;
if (sysInfo) {
this.setData({
statusBarHeight: sysInfo.statusBarHeight || 20,
navBarHeight: 44,
});
}
} catch (e) { /* 使用默认值 */ }
this.setData({ userId });
this.loadProfile();
},
async loadProfile() {
this.setData({ loading: true, loadError: '' });
try {
const user = await recommendationService.getUserDetail(this.data.userId);
this.setData({ loading: false, profile: user });
this._deriveDisplay(user);
} catch (err) {
this.setData({
loading: false,
loadError: (err && err.message) || '加载失败',
});
appStore.toastError(err, '加载用户资料失败');
}
},
/**
* 把后端 User 字段派生为展示用结构,避免 wxml 内做条件拼接
*/
_deriveDisplay(user) {
if (!user) return;
const relationStatus = user.relationStatus || RELATION_STATUS.NEUTRAL;
const isLiked = relationStatus === RELATION_STATUS.LIKED || relationStatus === RELATION_STATUS.MUTUAL_MATCH;
const isMutual = relationStatus === RELATION_STATUS.MUTUAL_MATCH;
// AGENTS 第 3 条:只有 Mutual 后才能聊天
const canChat = isMutual && relationStatus !== RELATION_STATUS.BLOCKED;
const badges = [];
if (user.verified || user.isRealNameVerified) {
badges.push({ kind: 'success', label: '已实名' });
}
if (user.reviewed || user.profileReviewStatus === 'approved') {
badges.push({ kind: 'info', label: '资料已审核' });
}
const metaParts = [];
if (user.heightCm) metaParts.push(`${user.heightCm}cm`);
if (user.regionName) metaParts.push(user.regionName);
if (user.educationCode) metaParts.push(user.educationCode);
if (user.occupationCode) metaParts.push(user.occupationCode);
const basicInfo = [];
if (user.maritalStatus) basicInfo.push({ label: '婚姻状况', value: user.maritalStatus });
if (user.childrenStatus || user.hasChildren !== undefined) {
basicInfo.push({ label: '子女情况', value: user.childrenStatus || (user.hasChildren ? '有孩' : '无孩') });
}
if (user.educationCode) basicInfo.push({ label: '学历', value: user.educationCode });
if (user.occupationCode) basicInfo.push({ label: '职业', value: user.occupationCode });
if (user.smokingStatus) basicInfo.push({ label: '吸烟', value: user.smokingStatus });
if (user.drinkingStatus) basicInfo.push({ label: '饮酒', value: user.drinkingStatus });
if (user.longTermDanyang) basicInfo.push({ label: '定居意愿', value: user.longTermDanyang, full: true });
if (user.futureDanyang) basicInfo.push({ label: '未来规划', value: user.futureDanyang, full: true });
const attitudeInfo = [];
if (user.relationshipGoal) attitudeInfo.push({ label: '寻找', value: user.relationshipGoal, full: true });
if (user.marriagePlan) attitudeInfo.push({ label: '结婚计划', value: user.marriagePlan, full: true });
this.setData({
relationStatus,
isLiked,
isMutual,
canChat,
displayName: user.displayName || '用户',
ageText: user.age ? `${user.age}岁` : '',
metaLine: metaParts.join(' · '),
badges,
basicInfo,
attitudeInfo,
});
},
onBack() {
wx.navigateBack();
},
onRetry() {
this.loadProfile();
},
/**
* 喜欢(AGENTS 第 3/5/6 条:用户本人操作,mutualMatch 时跳聊天)
*/
async onLike() {
const { userId, actionLoading, isLiked } = this.data;
if (!userId || actionLoading || isLiked) return;
this.setData({ actionLoading: true });
try {
const result = await matchStore.like(userId);
// 本地派生状态更新
if (result && result.mutualMatch) {
this.setData({
isLiked: true,
isMutual: true,
canChat: true,
relationStatus: RELATION_STATUS.MUTUAL_MATCH,
actionLoading: false,
});
appStore.toastSuccess('双向喜欢!可以开始聊天了');
if (result.matchedConversationId) {
wx.navigateTo({ url: to.chat(result.matchedConversationId) });
}
} else {
this.setData({
isLiked: true,
relationStatus: RELATION_STATUS.LIKED,
actionLoading: false,
});
appStore.toastSuccess('已表达喜欢');
}
} catch (err) {
this.setData({ actionLoading: false });
if (err && err.code === BUSINESS_ERROR_CODE.DUPLICATE_ACTION) {
appStore.toast('已表达过喜欢');
} else {
appStore.toastError(err, '操作失败');
}
}
},
/**
* 开始聊天(仅 Mutual 后可见,AGENTS 第 3 条)
*/
onGoChat() {
const { userId, canChat, profile } = this.data;
if (!canChat) {
appStore.toast('双方互相喜欢后才能开始聊天');
return;
}
// matchedConversationId 优先来自最近 like 结果;无则用 userId 占位由 chat 页拉取
const conversationId = (profile && profile.matchedConversationId) || userId;
wx.navigateTo({ url: to.chat(conversationId) });
},
/**
* 举报:跳转 report 页,不在本页处理(AGENTS 第 7 条:举报内容不在此暴露)
*/
onReport() {
const { userId } = this.data;
if (!userId) return;
wx.navigateTo({ url: to.report(userId) });
},
/**
* 拉黑入口:弹出 DangerActionDialog 二次确认(AGENTS 第 4 条)
*/
onBlock() {
if (!this.data.userId) return;
this.setData({ showBlockDialog: true });
},
onConfirmBlock() {
this.setData({ showBlockDialog: false });
this._doBlock();
},
onCancelBlock() {
this.setData({ showBlockDialog: false });
},
/**
* 执行拉黑:调用跨 Store 编排 blockUser
* - MatchStore:从推荐/Mutual 列表移除
* - MessageStore:会话立即标记不可发消息
*/
async _doBlock() {
const { userId, actionLoading } = this.data;
if (!userId || actionLoading) return;
this.setData({ actionLoading: true });
try {
appStore.showLoading('处理中…');
await blockUser(userId, '用户主动拉黑');
appStore.hideLoading();
appStore.toastSuccess('已拉黑');
// 拉黑后离开当前页(AGENTS 第 4 条:Block 即时禁止互动)
setTimeout(() => wx.navigateBack(), 800);
} catch (err) {
appStore.hideLoading();
this.setData({ actionLoading: false });
appStore.toastError(err, '拉黑失败');
}
},
});