mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
Revert "feat: enrich mutual friend identities in export dialog"
This reverts commit f3027da43885a67583099008991dbfc4def3f4d1.
This commit is contained in:
@@ -52,7 +52,6 @@ interface SnsContactIdentity {
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
}
|
||||
|
||||
interface ParsedLikeUser {
|
||||
@@ -80,7 +79,6 @@ interface ArkmeLikeDetail {
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
source: 'xml' | 'legacy'
|
||||
}
|
||||
|
||||
@@ -94,7 +92,6 @@ interface ArkmeCommentDetail {
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
content: string
|
||||
refCommentId: string
|
||||
refNickname?: string
|
||||
@@ -105,7 +102,6 @@ interface ArkmeCommentDetail {
|
||||
refRemark?: string
|
||||
refNickName?: string
|
||||
refDisplayName?: string
|
||||
refAvatarUrl?: string
|
||||
emojis?: { url: string; md5: string; width: number; height: number; encryptUrl?: string; aesKey?: string }[]
|
||||
source: 'xml' | 'legacy'
|
||||
}
|
||||
@@ -327,7 +323,6 @@ class SnsService {
|
||||
let alias: string | undefined
|
||||
let remark: string | undefined
|
||||
let nickName: string | undefined
|
||||
let avatarUrl = this.toOptionalString(cached?.avatarUrl)
|
||||
|
||||
try {
|
||||
const contactResult = await wcdbService.getContact(normalized)
|
||||
@@ -341,17 +336,6 @@ class SnsService {
|
||||
// 联系人补全失败不影响导出
|
||||
}
|
||||
|
||||
if (!avatarUrl) {
|
||||
try {
|
||||
const avatarResult = await wcdbService.getAvatarUrls([normalized])
|
||||
if (avatarResult.success && avatarResult.map) {
|
||||
avatarUrl = this.toOptionalString(avatarResult.map[normalized])
|
||||
}
|
||||
} catch {
|
||||
// 头像补全失败不影响导出
|
||||
}
|
||||
}
|
||||
|
||||
const displayName = remark || nickName || alias || cached?.displayName || normalized
|
||||
return {
|
||||
username: normalized,
|
||||
@@ -360,8 +344,7 @@ class SnsService {
|
||||
wechatId: alias,
|
||||
remark,
|
||||
nickName,
|
||||
displayName,
|
||||
avatarUrl
|
||||
displayName
|
||||
}
|
||||
})()
|
||||
identityCache.set(normalized, pending)
|
||||
@@ -429,7 +412,6 @@ class SnsService {
|
||||
remark: identity?.remark,
|
||||
nickName: identity?.nickName,
|
||||
displayName: identity?.displayName || nickname || username || '',
|
||||
avatarUrl: identity?.avatarUrl,
|
||||
source: likeSource
|
||||
})
|
||||
}
|
||||
@@ -501,7 +483,6 @@ class SnsService {
|
||||
remark: actor?.remark,
|
||||
nickName: actor?.nickName,
|
||||
displayName: actor?.displayName || nickname || username || '',
|
||||
avatarUrl: actor?.avatarUrl,
|
||||
content: comment.content || '',
|
||||
refCommentId: comment.refCommentId || '',
|
||||
refNickname: comment.refNickname || refActor?.displayName,
|
||||
@@ -512,7 +493,6 @@ class SnsService {
|
||||
refRemark: refActor?.remark,
|
||||
refNickName: refActor?.nickName,
|
||||
refDisplayName: refActor?.displayName,
|
||||
refAvatarUrl: refActor?.avatarUrl,
|
||||
emojis: comment.emojis,
|
||||
source: commentSource
|
||||
})
|
||||
@@ -1041,8 +1021,7 @@ class SnsService {
|
||||
const result = await wcdbService.getSnsTimeline(limit, offset, usernames, keyword, startTime, endTime)
|
||||
if (!result.success || !result.timeline || result.timeline.length === 0) return result
|
||||
|
||||
const identityCache = new Map<string, Promise<SnsContactIdentity | null>>()
|
||||
const enrichedTimeline = await Promise.all(result.timeline.map(async (post: any) => {
|
||||
const enrichedTimeline = result.timeline.map((post: any) => {
|
||||
const contact = this.contactCache.get(post.username)
|
||||
const isVideoPost = post.type === 15
|
||||
const videoKey = extractVideoKey(post.rawXml || '')
|
||||
@@ -1082,22 +1061,14 @@ class SnsService {
|
||||
finalComments = this.fixCommentRefs(dllComments)
|
||||
}
|
||||
|
||||
const normalizedPost: SnsPost = {
|
||||
...post,
|
||||
comments: finalComments
|
||||
}
|
||||
const { likesDetail, commentsDetail } = await this.buildArkmeInteractionDetails(normalizedPost, identityCache)
|
||||
|
||||
return {
|
||||
...post,
|
||||
avatarUrl: contact?.avatarUrl,
|
||||
nickname: post.nickname || contact?.displayName || post.username,
|
||||
media: fixedMedia,
|
||||
comments: finalComments,
|
||||
likesDetail,
|
||||
commentsDetail
|
||||
comments: finalComments
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
return { ...result, timeline: enrichedTimeline }
|
||||
}
|
||||
|
||||
@@ -623,22 +623,18 @@
|
||||
|
||||
.session-mutual-friends-row {
|
||||
display: grid;
|
||||
grid-template-columns: 36px minmax(0, 1fr) 72px 108px;
|
||||
gap: 12px;
|
||||
grid-template-columns: 36px minmax(120px, 0.82fr) max-content 56px 96px minmax(0, 1.28fr);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--border-color) 68%, transparent);
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
min-height: 72px;
|
||||
min-height: 42px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.unconfirmed {
|
||||
background: color-mix(in srgb, var(--bg-secondary) 60%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.session-mutual-friends-rank,
|
||||
@@ -652,32 +648,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user-avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user-main {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user-head {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.session-mutual-friends-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -717,30 +687,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.session-mutual-friends-identity-badge {
|
||||
border-radius: 999px;
|
||||
padding: 3px 8px;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: #92400e;
|
||||
border: 1px solid color-mix(in srgb, #d97706 34%, var(--border-color));
|
||||
background: color-mix(in srgb, #f59e0b 11%, var(--bg-secondary));
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.session-mutual-friends-identity {
|
||||
min-width: 0;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.25;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&.secondary {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.session-mutual-friends-desc {
|
||||
min-width: 0;
|
||||
color: var(--text-tertiary);
|
||||
@@ -2034,7 +1980,6 @@
|
||||
height: var(--contacts-default-list-height);
|
||||
overflow: hidden;
|
||||
padding: 0 0 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contacts-virtuoso {
|
||||
@@ -2052,43 +1997,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.contacts-action-rail {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 12px;
|
||||
width: max(var(--contacts-action-col-width), 184px);
|
||||
z-index: 18;
|
||||
pointer-events: none;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -12px;
|
||||
width: 12px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(to right, transparent, var(--bg-primary));
|
||||
}
|
||||
}
|
||||
|
||||
.contacts-action-rail-row {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: var(--contacts-row-height);
|
||||
padding: 0 0 4px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
pointer-events: none;
|
||||
|
||||
&.selected .contacts-action-rail-card {
|
||||
background: rgba(var(--primary-rgb), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.table-bottom-scrollbar {
|
||||
flex: 0 0 auto;
|
||||
overflow-x: auto;
|
||||
@@ -2183,6 +2091,10 @@
|
||||
&.selected .contact-item {
|
||||
background: rgba(var(--primary-rgb), 0.08);
|
||||
}
|
||||
|
||||
&.selected .row-action-cell {
|
||||
background: rgba(var(--primary-rgb), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
@@ -2562,30 +2474,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.row-action-cell,
|
||||
.contacts-action-rail-card {
|
||||
.row-action-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: calc(var(--contacts-row-height) - 4px);
|
||||
padding: 0 6px 0 0;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
width: var(--contacts-action-col-width);
|
||||
min-width: var(--contacts-action-col-width);
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
background: var(--bg-primary);
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
bottom: -12px;
|
||||
left: -12px;
|
||||
width: 12px;
|
||||
left: -8px;
|
||||
width: 8px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(to right, transparent, var(--bg-primary));
|
||||
}
|
||||
@@ -4253,19 +4161,13 @@
|
||||
}
|
||||
|
||||
.session-mutual-friends-row {
|
||||
grid-template-columns: 28px minmax(0, 1fr) 56px 72px;
|
||||
grid-template-columns: 30px minmax(88px, 0.9fr) max-content 44px 74px;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user {
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.session-mutual-friends-user-avatar {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
.session-mutual-friends-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.session-load-detail-row {
|
||||
|
||||
@@ -44,12 +44,11 @@ import {
|
||||
subscribeBackgroundTasks
|
||||
} from '../services/backgroundTaskMonitor'
|
||||
import { useContactTypeCountsStore } from '../stores/contactTypeCountsStore'
|
||||
import { Avatar } from '../components/Avatar'
|
||||
import { SnsPostItem } from '../components/Sns/SnsPostItem'
|
||||
import { ContactSnsTimelineDialog } from '../components/Sns/ContactSnsTimelineDialog'
|
||||
import { ExportDateRangeDialog } from '../components/Export/ExportDateRangeDialog'
|
||||
import { ExportDefaultsSettingsForm, type ExportDefaultsSettingsPatch } from '../components/Export/ExportDefaultsSettingsForm'
|
||||
import type { SnsCommentDetail, SnsLikeDetail, SnsPost } from '../types/sns'
|
||||
import type { SnsPost } from '../types/sns'
|
||||
import {
|
||||
cloneExportDateRange,
|
||||
createDefaultDateRange,
|
||||
@@ -73,7 +72,6 @@ type DisplayNamePreference = 'group-nickname' | 'remark' | 'nickname'
|
||||
|
||||
type TextExportFormat = 'chatlab' | 'chatlab-jsonl' | 'json' | 'arkme-json' | 'html' | 'txt' | 'excel' | 'weclone' | 'sql'
|
||||
type SnsTimelineExportFormat = 'json' | 'html' | 'arkmejson'
|
||||
const CONTACTS_ROW_HEIGHT = 76
|
||||
|
||||
interface ExportOptions {
|
||||
format: TextExportFormat
|
||||
@@ -515,12 +513,6 @@ const getAvatarLetter = (name: string): string => {
|
||||
return [...name][0] || '?'
|
||||
}
|
||||
|
||||
const toOptionalString = (value: unknown): string | undefined => {
|
||||
if (typeof value !== 'string') return undefined
|
||||
const trimmed = value.trim()
|
||||
return trimmed ? trimmed : undefined
|
||||
}
|
||||
|
||||
const toComparableNameSet = (values: Array<string | undefined | null>): Set<string> => {
|
||||
const set = new Set<string>()
|
||||
for (const value of values) {
|
||||
@@ -610,15 +602,7 @@ type SessionMutualFriendDirection = 'incoming' | 'outgoing' | 'bidirectional'
|
||||
type SessionMutualFriendBehavior = 'likes' | 'comments' | 'both'
|
||||
|
||||
interface SessionMutualFriendItem {
|
||||
key: string
|
||||
identityKey?: string
|
||||
name: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
avatarUrl?: string
|
||||
isConfirmed: boolean
|
||||
incomingLikeCount: number
|
||||
incomingCommentCount: number
|
||||
outgoingLikeCount: number
|
||||
@@ -637,63 +621,6 @@ interface SessionMutualFriendsMetric {
|
||||
computedAt: number
|
||||
}
|
||||
|
||||
const getSessionMutualFriendIdentityKey = (username?: string, wechatId?: string): string | undefined => {
|
||||
const normalizedUsername = toOptionalString(username)
|
||||
if (normalizedUsername) return `u:${normalizedUsername}`
|
||||
const normalizedWechatId = toOptionalString(wechatId)
|
||||
if (normalizedWechatId) return `w:${normalizedWechatId}`
|
||||
return undefined
|
||||
}
|
||||
|
||||
const getSessionMutualFriendFallbackKey = (name: string): string => `n:${name || '未知用户'}`
|
||||
|
||||
const resolveSessionMutualFriendName = (params: {
|
||||
displayName?: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
wechatId?: string
|
||||
nickname?: string
|
||||
username?: string
|
||||
}): string => {
|
||||
return (
|
||||
toOptionalString(params.displayName) ||
|
||||
toOptionalString(params.remark) ||
|
||||
toOptionalString(params.nickName) ||
|
||||
toOptionalString(params.wechatId) ||
|
||||
toOptionalString(params.nickname) ||
|
||||
toOptionalString(params.username) ||
|
||||
'未知用户'
|
||||
)
|
||||
}
|
||||
|
||||
const applySessionMutualFriendDerivedState = (item: SessionMutualFriendItem): void => {
|
||||
const incomingTotal = item.incomingLikeCount + item.incomingCommentCount
|
||||
const outgoingTotal = item.outgoingLikeCount + item.outgoingCommentCount
|
||||
item.direction = incomingTotal > 0 && outgoingTotal > 0
|
||||
? 'bidirectional'
|
||||
: incomingTotal > 0
|
||||
? 'incoming'
|
||||
: 'outgoing'
|
||||
item.behavior = summarizeMutualFriendBehavior(
|
||||
item.incomingLikeCount + item.outgoingLikeCount,
|
||||
item.incomingCommentCount + item.outgoingCommentCount
|
||||
)
|
||||
}
|
||||
|
||||
const mergeSessionMutualFriendItemProfile = (
|
||||
target: SessionMutualFriendItem,
|
||||
profile: Partial<Pick<SessionMutualFriendItem, 'identityKey' | 'username' | 'wxid' | 'wechatId' | 'remark' | 'avatarUrl' | 'name' | 'isConfirmed'>>
|
||||
): void => {
|
||||
if (profile.identityKey && !target.identityKey) target.identityKey = profile.identityKey
|
||||
if (profile.username && !target.username) target.username = profile.username
|
||||
if (profile.wxid && !target.wxid) target.wxid = profile.wxid
|
||||
if (profile.wechatId && !target.wechatId) target.wechatId = profile.wechatId
|
||||
if (profile.remark && !target.remark) target.remark = profile.remark
|
||||
if (profile.avatarUrl && !target.avatarUrl) target.avatarUrl = profile.avatarUrl
|
||||
if (profile.name && (!target.name || !target.isConfirmed)) target.name = profile.name
|
||||
if (profile.isConfirmed) target.isConfirmed = true
|
||||
}
|
||||
|
||||
interface SessionSnsRankCacheEntry {
|
||||
likes: SessionSnsRankItem[]
|
||||
comments: SessionSnsRankItem[]
|
||||
@@ -751,121 +678,55 @@ const buildSessionMutualFriendsMetric = (
|
||||
): SessionMutualFriendsMetric => {
|
||||
const friendMap = new Map<string, SessionMutualFriendItem>()
|
||||
|
||||
const ensureItem = (seed: {
|
||||
name: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
avatarUrl?: string
|
||||
identityKey?: string
|
||||
isConfirmed: boolean
|
||||
}): SessionMutualFriendItem => {
|
||||
const key = seed.identityKey || getSessionMutualFriendFallbackKey(seed.name)
|
||||
const existing = friendMap.get(key)
|
||||
if (existing) {
|
||||
mergeSessionMutualFriendItemProfile(existing, seed)
|
||||
return existing
|
||||
}
|
||||
for (const post of posts) {
|
||||
const createTime = Number(post?.createTime) || 0
|
||||
const likes = Array.isArray(post?.likes) ? post.likes : []
|
||||
const comments = Array.isArray(post?.comments) ? post.comments : []
|
||||
|
||||
const created: SessionMutualFriendItem = {
|
||||
key,
|
||||
identityKey: seed.identityKey,
|
||||
name: seed.name,
|
||||
username: seed.username,
|
||||
wxid: seed.wxid,
|
||||
wechatId: seed.wechatId,
|
||||
remark: seed.remark,
|
||||
avatarUrl: seed.avatarUrl,
|
||||
isConfirmed: seed.isConfirmed,
|
||||
incomingLikeCount: 0,
|
||||
for (const likeNameRaw of likes) {
|
||||
const name = String(likeNameRaw || '').trim() || '未知用户'
|
||||
const existing = friendMap.get(name)
|
||||
if (existing) {
|
||||
existing.incomingLikeCount += 1
|
||||
existing.totalCount += 1
|
||||
existing.behavior = existing.incomingCommentCount > 0 ? 'both' : 'likes'
|
||||
if (createTime > existing.latestTime) existing.latestTime = createTime
|
||||
continue
|
||||
}
|
||||
friendMap.set(name, {
|
||||
name,
|
||||
incomingLikeCount: 1,
|
||||
incomingCommentCount: 0,
|
||||
outgoingLikeCount: 0,
|
||||
outgoingCommentCount: 0,
|
||||
totalCount: 0,
|
||||
latestTime: 0,
|
||||
totalCount: 1,
|
||||
latestTime: createTime,
|
||||
direction: 'incoming',
|
||||
behavior: 'likes'
|
||||
}
|
||||
friendMap.set(key, created)
|
||||
return created
|
||||
}
|
||||
|
||||
for (const post of posts) {
|
||||
const createTime = Number(post?.createTime) || 0
|
||||
const likesDetail = Array.isArray(post?.likesDetail) && post.likesDetail.length > 0
|
||||
? post.likesDetail
|
||||
: (Array.isArray(post?.likes) ? post.likes : []).map((likeNameRaw): SnsLikeDetail => ({
|
||||
nickname: String(likeNameRaw || '').trim() || '未知用户',
|
||||
displayName: String(likeNameRaw || '').trim() || '未知用户',
|
||||
source: 'legacy'
|
||||
}))
|
||||
const commentsDetail = Array.isArray(post?.commentsDetail) && post.commentsDetail.length > 0
|
||||
? post.commentsDetail
|
||||
: (Array.isArray(post?.comments) ? post.comments : []).map((comment): SnsCommentDetail => ({
|
||||
id: String(comment?.id || ''),
|
||||
nickname: String(comment?.nickname || '').trim() || '未知用户',
|
||||
displayName: String(comment?.nickname || '').trim() || '未知用户',
|
||||
content: String(comment?.content || ''),
|
||||
refCommentId: String(comment?.refCommentId || ''),
|
||||
refNickname: comment?.refNickname,
|
||||
refUsername: comment?.refUsername,
|
||||
emojis: comment?.emojis,
|
||||
source: 'legacy'
|
||||
}))
|
||||
|
||||
for (const like of likesDetail) {
|
||||
const username = toOptionalString(like.username || like.wxid)
|
||||
const wechatId = toOptionalString(like.wechatId || like.alias)
|
||||
const identityKey = getSessionMutualFriendIdentityKey(username, wechatId)
|
||||
const existing = ensureItem({
|
||||
name: resolveSessionMutualFriendName({
|
||||
displayName: like.displayName,
|
||||
remark: like.remark,
|
||||
nickName: like.nickName,
|
||||
wechatId,
|
||||
nickname: like.nickname,
|
||||
username
|
||||
}),
|
||||
username,
|
||||
wxid: username,
|
||||
wechatId,
|
||||
remark: toOptionalString(like.remark),
|
||||
avatarUrl: toOptionalString(like.avatarUrl),
|
||||
identityKey,
|
||||
isConfirmed: Boolean(identityKey && username)
|
||||
})
|
||||
existing.incomingLikeCount += 1
|
||||
existing.totalCount += 1
|
||||
if (createTime > existing.latestTime) existing.latestTime = createTime
|
||||
applySessionMutualFriendDerivedState(existing)
|
||||
}
|
||||
|
||||
for (const comment of commentsDetail) {
|
||||
const username = toOptionalString(comment.username || comment.wxid)
|
||||
const wechatId = toOptionalString(comment.wechatId || comment.alias)
|
||||
const identityKey = getSessionMutualFriendIdentityKey(username, wechatId)
|
||||
const existing = ensureItem({
|
||||
name: resolveSessionMutualFriendName({
|
||||
displayName: comment.displayName,
|
||||
remark: comment.remark,
|
||||
nickName: comment.nickName,
|
||||
wechatId,
|
||||
nickname: comment.nickname,
|
||||
username
|
||||
}),
|
||||
username,
|
||||
wxid: username,
|
||||
wechatId,
|
||||
remark: toOptionalString(comment.remark),
|
||||
avatarUrl: toOptionalString(comment.avatarUrl),
|
||||
identityKey,
|
||||
isConfirmed: Boolean(identityKey && username)
|
||||
})
|
||||
for (const comment of comments) {
|
||||
const name = String(comment?.nickname || '').trim() || '未知用户'
|
||||
const existing = friendMap.get(name)
|
||||
if (existing) {
|
||||
existing.incomingCommentCount += 1
|
||||
existing.totalCount += 1
|
||||
existing.behavior = existing.incomingLikeCount > 0 ? 'both' : 'comments'
|
||||
if (createTime > existing.latestTime) existing.latestTime = createTime
|
||||
applySessionMutualFriendDerivedState(existing)
|
||||
continue
|
||||
}
|
||||
friendMap.set(name, {
|
||||
name,
|
||||
incomingLikeCount: 0,
|
||||
incomingCommentCount: 1,
|
||||
outgoingLikeCount: 0,
|
||||
outgoingCommentCount: 0,
|
||||
totalCount: 1,
|
||||
latestTime: createTime,
|
||||
direction: 'incoming',
|
||||
behavior: 'comments'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1652,8 +1513,6 @@ function ExportPage() {
|
||||
const [nowTick, setNowTick] = useState(Date.now())
|
||||
const [isContactsListAtTop, setIsContactsListAtTop] = useState(true)
|
||||
const [isContactsHeaderDragging, setIsContactsHeaderDragging] = useState(false)
|
||||
const [contactsListScrollTop, setContactsListScrollTop] = useState(0)
|
||||
const [contactsVisibleRange, setContactsVisibleRange] = useState({ startIndex: 0, endIndex: -1 })
|
||||
const [contactsHorizontalScrollMetrics, setContactsHorizontalScrollMetrics] = useState({
|
||||
viewportWidth: 0,
|
||||
contentWidth: 0
|
||||
@@ -2929,27 +2788,21 @@ function ExportPage() {
|
||||
|
||||
const getSessionMutualFriendProfile = useCallback((sessionId: string): {
|
||||
displayName: string
|
||||
remark?: string
|
||||
wechatId?: string
|
||||
avatarUrl?: string
|
||||
primaryIdentityKey: string
|
||||
candidateIdentityKeys: Set<string>
|
||||
candidateNames: Set<string>
|
||||
} => {
|
||||
const normalizedSessionId = String(sessionId || '').trim()
|
||||
const contact = contactsList.find(item => item.username === normalizedSessionId)
|
||||
const session = sessionsRef.current.find(item => item.username === normalizedSessionId)
|
||||
const displayName = contact?.displayName || contact?.remark || contact?.nickname || session?.displayName || normalizedSessionId
|
||||
const wechatId = toOptionalString(contact?.alias)
|
||||
const candidateIdentityKeys = new Set<string>()
|
||||
candidateIdentityKeys.add(`u:${normalizedSessionId}`)
|
||||
if (wechatId) candidateIdentityKeys.add(`w:${wechatId}`)
|
||||
return {
|
||||
displayName,
|
||||
remark: toOptionalString(contact?.remark),
|
||||
wechatId,
|
||||
avatarUrl: toOptionalString(contact?.avatarUrl),
|
||||
primaryIdentityKey: `u:${normalizedSessionId}`,
|
||||
candidateIdentityKeys
|
||||
candidateNames: toComparableNameSet([
|
||||
displayName,
|
||||
contact?.displayName,
|
||||
contact?.remark,
|
||||
contact?.nickname,
|
||||
contact?.alias
|
||||
])
|
||||
}
|
||||
}, [contactsList])
|
||||
|
||||
@@ -2961,54 +2814,41 @@ function ExportPage() {
|
||||
const directMetric = directMetrics[normalizedTargetSessionId]
|
||||
if (!directMetric) return null
|
||||
|
||||
const targetProfile = getSessionMutualFriendProfile(normalizedTargetSessionId)
|
||||
const { candidateNames } = getSessionMutualFriendProfile(normalizedTargetSessionId)
|
||||
const mergedMap = new Map<string, SessionMutualFriendItem>()
|
||||
for (const item of directMetric.items) {
|
||||
mergedMap.set(item.key, { ...item })
|
||||
mergedMap.set(item.name, { ...item })
|
||||
}
|
||||
|
||||
for (const [sourceSessionId, sourceMetric] of Object.entries(directMetrics)) {
|
||||
if (!sourceMetric || sourceSessionId === normalizedTargetSessionId) continue
|
||||
const sourceProfile = getSessionMutualFriendProfile(sourceSessionId)
|
||||
if (!sourceProfile.displayName) continue
|
||||
const reverseMatches = sourceMetric.items.filter(item => {
|
||||
if (!item.identityKey) return false
|
||||
return targetProfile.candidateIdentityKeys.has(item.identityKey)
|
||||
})
|
||||
if (mergedMap.has(sourceProfile.displayName)) continue
|
||||
|
||||
const reverseMatches = sourceMetric.items.filter(item => candidateNames.has(item.name))
|
||||
if (reverseMatches.length === 0) continue
|
||||
|
||||
const reverseCount = reverseMatches.reduce((sum, item) => sum + item.totalCount, 0)
|
||||
const reverseLikeCount = reverseMatches.reduce((sum, item) => sum + item.incomingLikeCount, 0)
|
||||
const reverseCommentCount = reverseMatches.reduce((sum, item) => sum + item.incomingCommentCount, 0)
|
||||
const reverseLatestTime = reverseMatches.reduce((latest, item) => Math.max(latest, item.latestTime), 0)
|
||||
const existing = mergedMap.get(sourceProfile.primaryIdentityKey)
|
||||
const existing = mergedMap.get(sourceProfile.displayName)
|
||||
if (existing) {
|
||||
mergeSessionMutualFriendItemProfile(existing, {
|
||||
identityKey: sourceProfile.primaryIdentityKey,
|
||||
username: sourceSessionId,
|
||||
wxid: sourceSessionId,
|
||||
wechatId: sourceProfile.wechatId,
|
||||
remark: sourceProfile.remark,
|
||||
avatarUrl: sourceProfile.avatarUrl,
|
||||
name: sourceProfile.displayName,
|
||||
isConfirmed: true
|
||||
})
|
||||
existing.outgoingLikeCount += reverseLikeCount
|
||||
existing.outgoingCommentCount += reverseCommentCount
|
||||
existing.totalCount += reverseCount
|
||||
existing.latestTime = Math.max(existing.latestTime, reverseLatestTime)
|
||||
applySessionMutualFriendDerivedState(existing)
|
||||
existing.direction = (existing.incomingLikeCount + existing.incomingCommentCount) > 0
|
||||
? 'bidirectional'
|
||||
: 'outgoing'
|
||||
existing.behavior = summarizeMutualFriendBehavior(
|
||||
existing.incomingLikeCount + existing.outgoingLikeCount,
|
||||
existing.incomingCommentCount + existing.outgoingCommentCount
|
||||
)
|
||||
} else {
|
||||
const created: SessionMutualFriendItem = {
|
||||
key: sourceProfile.primaryIdentityKey,
|
||||
identityKey: sourceProfile.primaryIdentityKey,
|
||||
mergedMap.set(sourceProfile.displayName, {
|
||||
name: sourceProfile.displayName,
|
||||
username: sourceSessionId,
|
||||
wxid: sourceSessionId,
|
||||
wechatId: sourceProfile.wechatId,
|
||||
remark: sourceProfile.remark,
|
||||
avatarUrl: sourceProfile.avatarUrl,
|
||||
isConfirmed: true,
|
||||
incomingLikeCount: 0,
|
||||
incomingCommentCount: 0,
|
||||
outgoingLikeCount: reverseLikeCount,
|
||||
@@ -3017,8 +2857,7 @@ function ExportPage() {
|
||||
latestTime: reverseLatestTime,
|
||||
direction: 'outgoing',
|
||||
behavior: summarizeMutualFriendBehavior(reverseLikeCount, reverseCommentCount)
|
||||
}
|
||||
mergedMap.set(created.key, created)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3048,7 +2887,7 @@ function ExportPage() {
|
||||
for (const targetSessionId of allSessionIds) {
|
||||
if (targetSessionId === normalizedSessionId) continue
|
||||
const targetProfile = getSessionMutualFriendProfile(targetSessionId)
|
||||
if (directMetric.items.some(item => item.identityKey && targetProfile.candidateIdentityKeys.has(item.identityKey))) {
|
||||
if (directMetric.items.some(item => targetProfile.candidateNames.has(item.name))) {
|
||||
impactedSessionIds.add(targetSessionId)
|
||||
}
|
||||
}
|
||||
@@ -4995,11 +4834,6 @@ function ExportPage() {
|
||||
const endIndex = Number.isFinite(range?.endIndex) ? Math.max(startIndex, Math.floor(range.endIndex)) : startIndex
|
||||
sessionMediaMetricVisibleRangeRef.current = { startIndex, endIndex }
|
||||
sessionMutualFriendsVisibleRangeRef.current = { startIndex, endIndex }
|
||||
setContactsVisibleRange(prev => (
|
||||
prev.startIndex === startIndex && prev.endIndex === endIndex
|
||||
? prev
|
||||
: { startIndex, endIndex }
|
||||
))
|
||||
if (isLoadingSessionCountsRef.current || !isSessionCountStageReady) return
|
||||
const visibleTargets = collectVisibleSessionMetricTargets(filteredContacts)
|
||||
if (visibleTargets.length === 0) return
|
||||
@@ -5203,18 +5037,7 @@ function ExportPage() {
|
||||
const items = sessionMutualFriendsDialogMetric?.items || []
|
||||
const keyword = sessionMutualFriendsSearch.trim().toLowerCase()
|
||||
if (!keyword) return items
|
||||
return items.filter((item) => {
|
||||
const haystack = [
|
||||
item.name,
|
||||
item.remark,
|
||||
item.wechatId,
|
||||
item.wxid,
|
||||
item.username
|
||||
]
|
||||
.map(value => String(value || '').toLowerCase())
|
||||
.join(' ')
|
||||
return haystack.includes(keyword)
|
||||
})
|
||||
return items.filter(item => item.name.toLowerCase().includes(keyword))
|
||||
}, [sessionMutualFriendsDialogMetric, sessionMutualFriendsSearch])
|
||||
|
||||
const applySessionDetailStats = useCallback((
|
||||
@@ -5795,7 +5618,7 @@ function ExportPage() {
|
||||
const taskCenterAlertCount = taskRunningCount + taskQueuedCount
|
||||
const hasFilteredContacts = filteredContacts.length > 0
|
||||
const contactsTableMinWidth = useMemo(() => {
|
||||
const baseWidth = 24 + 34 + 44 + 160 + 120 + (4 * 72) + (7 * 12)
|
||||
const baseWidth = 24 + 34 + 44 + 280 + 120 + (4 * 72) + 140 + (8 * 12)
|
||||
const snsWidth = shouldShowSnsColumn ? 72 + 12 : 0
|
||||
const mutualFriendsWidth = shouldShowMutualFriendsColumn ? 72 + 12 : 0
|
||||
return baseWidth + snsWidth + mutualFriendsWidth
|
||||
@@ -5991,6 +5814,11 @@ function ExportPage() {
|
||||
const canExport = Boolean(matchedSession?.hasSession)
|
||||
const isSessionBindingPending = !matchedSession && (isLoading || isSessionEnriching)
|
||||
const checked = canExport && selectedSessions.has(contact.username)
|
||||
const isRunning = canExport && runningSessionIds.has(contact.username)
|
||||
const isQueued = canExport && queuedSessionIds.has(contact.username)
|
||||
const recentExportTimestamp = lastExportBySession[contact.username]
|
||||
const hasRecentExport = canExport && Boolean(recentExportTimestamp)
|
||||
const recentExportTime = hasRecentExport ? formatRecentExportTime(recentExportTimestamp, nowTick) : ''
|
||||
const countedMessages = normalizeMessageCount(sessionMessageCounts[contact.username])
|
||||
const hintedMessages = normalizeMessageCount(matchedSession?.messageCountHint)
|
||||
const displayedMessageCount = countedMessages ?? hintedMessages
|
||||
@@ -6167,60 +5995,7 @@ function ExportPage() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [
|
||||
openContactSnsTimeline,
|
||||
openSessionDetail,
|
||||
openSessionMutualFriendsDialog,
|
||||
selectedSessions,
|
||||
sessionContentMetrics,
|
||||
sessionMutualFriendsMetrics,
|
||||
sessionLoadTraceMap,
|
||||
sessionMessageCounts,
|
||||
sessionRowByUsername,
|
||||
isLoading,
|
||||
isSessionEnriching,
|
||||
showSessionDetailPanel,
|
||||
shouldShowMutualFriendsColumn,
|
||||
shouldShowSnsColumn,
|
||||
snsUserPostCounts,
|
||||
snsUserPostCountsStatus,
|
||||
toggleSelectSession
|
||||
])
|
||||
const visibleContactsForActionRail = useMemo(() => {
|
||||
if (!hasFilteredContacts || contactsVisibleRange.endIndex < contactsVisibleRange.startIndex || contactsVisibleRange.endIndex < 0) return []
|
||||
const startIndex = Math.max(0, Math.min(filteredContacts.length - 1, contactsVisibleRange.startIndex))
|
||||
const endIndex = Math.max(startIndex, Math.min(filteredContacts.length - 1, contactsVisibleRange.endIndex))
|
||||
if (!Number.isFinite(startIndex) || !Number.isFinite(endIndex) || endIndex < startIndex) return []
|
||||
return filteredContacts.slice(startIndex, endIndex + 1).map((contact, offset) => {
|
||||
const index = startIndex + offset
|
||||
return {
|
||||
contact,
|
||||
index,
|
||||
top: (index * CONTACTS_ROW_HEIGHT) - contactsListScrollTop
|
||||
}
|
||||
})
|
||||
}, [contactsListScrollTop, contactsVisibleRange.endIndex, contactsVisibleRange.startIndex, filteredContacts, hasFilteredContacts])
|
||||
const renderContactActionRailItem = useCallback((contact: ContactInfo, index: number, top: number) => {
|
||||
const matchedSession = sessionRowByUsername.get(contact.username)
|
||||
const canExport = Boolean(matchedSession?.hasSession)
|
||||
const checked = canExport && selectedSessions.has(contact.username)
|
||||
const isRunning = canExport && runningSessionIds.has(contact.username)
|
||||
const isQueued = canExport && queuedSessionIds.has(contact.username)
|
||||
const recentExportTimestamp = lastExportBySession[contact.username]
|
||||
const hasRecentExport = canExport && Boolean(recentExportTimestamp)
|
||||
const recentExportTime = hasRecentExport ? formatRecentExportTime(recentExportTimestamp, nowTick) : ''
|
||||
|
||||
return (
|
||||
<div
|
||||
key={contact.username}
|
||||
className={`contacts-action-rail-row ${checked ? 'selected' : ''}`}
|
||||
style={{ top: `${top}px` }}
|
||||
data-index={index}
|
||||
>
|
||||
<div className="contacts-action-rail-card">
|
||||
<div className="row-action-cell">
|
||||
<div className={`row-action-main ${hasRecentExport ? '' : 'single-line'}`.trim()}>
|
||||
<div className={`row-export-action-stack ${hasRecentExport ? '' : 'single-line'}`.trim()}>
|
||||
<button
|
||||
@@ -6248,17 +6023,32 @@ function ExportPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [
|
||||
lastExportBySession,
|
||||
nowTick,
|
||||
openContactSnsTimeline,
|
||||
openSessionDetail,
|
||||
openSessionMutualFriendsDialog,
|
||||
openSingleExport,
|
||||
queuedSessionIds,
|
||||
runningSessionIds,
|
||||
selectedSessions,
|
||||
sessionDetail?.wxid,
|
||||
sessionRowByUsername
|
||||
sessionContentMetrics,
|
||||
sessionMutualFriendsMetrics,
|
||||
sessionLoadTraceMap,
|
||||
sessionMessageCounts,
|
||||
sessionRowByUsername,
|
||||
isLoading,
|
||||
isSessionEnriching,
|
||||
showSessionDetailPanel,
|
||||
shouldShowMutualFriendsColumn,
|
||||
shouldShowSnsColumn,
|
||||
snsUserPostCounts,
|
||||
snsUserPostCountsStatus,
|
||||
toggleSelectSession
|
||||
])
|
||||
const handleContactsListWheelCapture = useCallback((event: WheelEvent<HTMLDivElement>) => {
|
||||
const deltaY = event.deltaY
|
||||
@@ -6277,19 +6067,9 @@ function ExportPage() {
|
||||
window.scrollBy({ top: deltaY, behavior: 'auto' })
|
||||
}
|
||||
}, [isContactsListAtTop])
|
||||
const handleContactsListScrollCapture = useCallback((event: UIEvent<HTMLDivElement>) => {
|
||||
const target = event.target
|
||||
if (!(target instanceof HTMLDivElement)) return
|
||||
const nextScrollTop = Math.max(0, target.scrollTop)
|
||||
setContactsListScrollTop(prev => (
|
||||
Math.abs(prev - nextScrollTop) > 1 ? nextScrollTop : prev
|
||||
))
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (hasFilteredContacts) return
|
||||
setIsContactsListAtTop(true)
|
||||
setContactsListScrollTop(0)
|
||||
setContactsVisibleRange({ startIndex: 0, endIndex: -1 })
|
||||
}, [hasFilteredContacts])
|
||||
const chooseExportFolder = useCallback(async () => {
|
||||
const result = await window.electronAPI.dialog.openFile({
|
||||
@@ -6701,24 +6481,18 @@ function ExportPage() {
|
||||
<div
|
||||
className="contacts-list"
|
||||
onWheelCapture={handleContactsListWheelCapture}
|
||||
onScrollCapture={handleContactsListScrollCapture}
|
||||
>
|
||||
<Virtuoso
|
||||
ref={contactsVirtuosoRef}
|
||||
className="contacts-virtuoso"
|
||||
data={filteredContacts}
|
||||
computeItemKey={(_, contact) => contact.username}
|
||||
fixedItemHeight={CONTACTS_ROW_HEIGHT}
|
||||
fixedItemHeight={76}
|
||||
itemContent={renderContactRow}
|
||||
rangeChanged={handleContactsRangeChanged}
|
||||
atTopStateChange={setIsContactsListAtTop}
|
||||
overscan={420}
|
||||
/>
|
||||
<div className="contacts-action-rail">
|
||||
{visibleContactsForActionRail.map(({ contact, index, top }) => (
|
||||
renderContactActionRailItem(contact, index, top)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -7016,15 +6790,15 @@ function ExportPage() {
|
||||
</div>
|
||||
|
||||
<div className="session-mutual-friends-tip">
|
||||
打开桌面端微信进入对方朋友圈,刷得越多这里聚合得越全。已确认身份的共同好友会展示头像、备注、微信号;未确认的旧数据仅保留名字。
|
||||
打开桌面端微信,进入到这个人的朋友圈中,刷ta 的朋友圈,刷的越多这里的数据聚合越多
|
||||
</div>
|
||||
|
||||
<div className="session-mutual-friends-toolbar">
|
||||
<input
|
||||
value={sessionMutualFriendsSearch}
|
||||
onChange={(event) => setSessionMutualFriendsSearch(event.target.value)}
|
||||
placeholder="搜索共同好友(备注 / 微信号 / wxid)"
|
||||
aria-label="搜索共同好友(备注 / 微信号 / wxid)"
|
||||
placeholder="搜索共同好友"
|
||||
aria-label="搜索共同好友"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7036,38 +6810,14 @@ function ExportPage() {
|
||||
) : (
|
||||
<div className="session-mutual-friends-list">
|
||||
{filteredSessionMutualFriendsDialogItems.map((item, index) => (
|
||||
<div className={`session-mutual-friends-row ${item.isConfirmed ? 'confirmed' : 'unconfirmed'}`} key={`${sessionMutualFriendsDialogTarget.username}-${item.key}`}>
|
||||
<div className="session-mutual-friends-row" key={`${sessionMutualFriendsDialogTarget.username}-${item.name}`}>
|
||||
<span className="session-mutual-friends-rank">{index + 1}</span>
|
||||
<div className="session-mutual-friends-user">
|
||||
<Avatar
|
||||
src={item.avatarUrl}
|
||||
name={item.name}
|
||||
size={40}
|
||||
shape="rounded"
|
||||
className="session-mutual-friends-user-avatar"
|
||||
/>
|
||||
<div className="session-mutual-friends-user-main">
|
||||
<div className="session-mutual-friends-user-head">
|
||||
<span className="session-mutual-friends-name" title={item.name}>{item.name}</span>
|
||||
<span className={`session-mutual-friends-source ${item.direction}`}>
|
||||
{getSessionMutualFriendDirectionLabel(item.direction)}
|
||||
</span>
|
||||
{!item.isConfirmed && (
|
||||
<span className="session-mutual-friends-identity-badge">身份未确认</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="session-mutual-friends-identity" title={item.isConfirmed ? (item.wechatId ? `微信号: ${item.wechatId}` : '微信号: 未设置') : '仅从旧数据中解析到名字'}>
|
||||
{item.isConfirmed ? (
|
||||
item.wechatId ? `微信号: ${item.wechatId}` : '微信号: 未设置'
|
||||
) : (
|
||||
'仅从旧数据中解析到名字'
|
||||
)}
|
||||
</div>
|
||||
<div className="session-mutual-friends-identity secondary" title={item.isConfirmed ? `wxid: ${item.wxid || item.username || ''}` : '没有可用的 wxid,未做自动匹配'}>
|
||||
{item.isConfirmed
|
||||
? `wxid: ${item.wxid || item.username || ''}`
|
||||
: '没有可用的 wxid,未做自动匹配'}
|
||||
</div>
|
||||
<span className="session-mutual-friends-count">{item.totalCount.toLocaleString('zh-CN')}</span>
|
||||
<span className="session-mutual-friends-latest">{formatYmdDateFromSeconds(item.latestTime)}</span>
|
||||
<span
|
||||
className="session-mutual-friends-desc"
|
||||
title={describeSessionMutualFriendRelation(item, sessionMutualFriendsDialogTarget.displayName)}
|
||||
@@ -7075,10 +6825,6 @@ function ExportPage() {
|
||||
{describeSessionMutualFriendRelation(item, sessionMutualFriendsDialogTarget.displayName)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="session-mutual-friends-count">{item.totalCount.toLocaleString('zh-CN')}</span>
|
||||
<span className="session-mutual-friends-latest">{formatYmdDateFromSeconds(item.latestTime)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
48
src/types/electron.d.ts
vendored
48
src/types/electron.d.ts
vendored
@@ -767,53 +767,7 @@ export interface ElectronAPI {
|
||||
}
|
||||
}>
|
||||
likes: Array<string>
|
||||
comments: Array<{
|
||||
id: string
|
||||
nickname: string
|
||||
username?: string
|
||||
content: string
|
||||
refCommentId: string
|
||||
refNickname?: string
|
||||
refUsername?: string
|
||||
emojis?: Array<{ url: string; md5: string; width: number; height: number; encryptUrl?: string; aesKey?: string }>
|
||||
}>
|
||||
likesDetail?: Array<{
|
||||
nickname: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
alias?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
source: 'xml' | 'legacy'
|
||||
}>
|
||||
commentsDetail?: Array<{
|
||||
id: string
|
||||
nickname: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
alias?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
content: string
|
||||
refCommentId: string
|
||||
refNickname?: string
|
||||
refUsername?: string
|
||||
refWxid?: string
|
||||
refAlias?: string
|
||||
refWechatId?: string
|
||||
refRemark?: string
|
||||
refNickName?: string
|
||||
refDisplayName?: string
|
||||
refAvatarUrl?: string
|
||||
emojis?: Array<{ url: string; md5: string; width: number; height: number; encryptUrl?: string; aesKey?: string }>
|
||||
source: 'xml' | 'legacy'
|
||||
}>
|
||||
comments: Array<{ id: string; nickname: string; content: string; refCommentId: string; refNickname?: string; emojis?: Array<{ url: string; md5: string; width: number; height: number; encryptUrl?: string; aesKey?: string }> }>
|
||||
rawXml?: string
|
||||
}>
|
||||
error?: string
|
||||
|
||||
@@ -28,53 +28,12 @@ export interface SnsCommentEmoji {
|
||||
export interface SnsComment {
|
||||
id: string
|
||||
nickname: string
|
||||
username?: string
|
||||
content: string
|
||||
refCommentId: string
|
||||
refNickname?: string
|
||||
refUsername?: string
|
||||
emojis?: SnsCommentEmoji[]
|
||||
}
|
||||
|
||||
export interface SnsLikeDetail {
|
||||
nickname: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
alias?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
source: 'xml' | 'legacy'
|
||||
}
|
||||
|
||||
export interface SnsCommentDetail {
|
||||
id: string
|
||||
nickname: string
|
||||
username?: string
|
||||
wxid?: string
|
||||
alias?: string
|
||||
wechatId?: string
|
||||
remark?: string
|
||||
nickName?: string
|
||||
displayName: string
|
||||
avatarUrl?: string
|
||||
content: string
|
||||
refCommentId: string
|
||||
refNickname?: string
|
||||
refUsername?: string
|
||||
refWxid?: string
|
||||
refAlias?: string
|
||||
refWechatId?: string
|
||||
refRemark?: string
|
||||
refNickName?: string
|
||||
refDisplayName?: string
|
||||
refAvatarUrl?: string
|
||||
emojis?: SnsCommentEmoji[]
|
||||
source: 'xml' | 'legacy'
|
||||
}
|
||||
|
||||
export interface SnsPost {
|
||||
id: string
|
||||
tid?: string // 数据库主键(雪花 ID),用于精确删除
|
||||
@@ -87,8 +46,6 @@ export interface SnsPost {
|
||||
media: SnsMedia[]
|
||||
likes: string[]
|
||||
comments: SnsComment[]
|
||||
likesDetail?: SnsLikeDetail[]
|
||||
commentsDetail?: SnsCommentDetail[]
|
||||
rawXml?: string
|
||||
linkTitle?: string
|
||||
linkUrl?: string
|
||||
|
||||
Reference in New Issue
Block a user