import { Provide } from '@midwayjs/core'; import { BaseService, CoolCommException } from '@cool-midway/core'; import { InjectEntityModel } from '@midwayjs/typeorm'; import { Repository } from 'typeorm'; import { CardEventEntity } from '../entity/event'; import { CardEventMemberEntity } from '../entity/event-member'; import { CardInfoEntity } from '../entity/info'; import { CardContact, CardEvent, CardEventJoinResult, CardEventMember, } from '../../../comm/types/omni'; export interface JoinEventInput { userId: number; code: string; cardId?: number | null; } export interface PageEventFloorQuery { userId: number; page?: number; size?: number; keyWord?: string; } export function normalizeEventCode(raw: string): string { const code = String(raw || '') .trim() .toUpperCase(); if (!/^[A-Z0-9]{6}$/.test(code)) { throw new CoolCommException('请输入 6 位活动码', 400); } return code; } function toEvent(row: CardEventEntity): CardEvent { return { id: row.id, code: row.code, name: row.name, status: row.status, }; } function toMember(row: CardEventMemberEntity): CardEventMember { return { id: row.id, eventId: row.eventId, userId: row.userId, cardId: row.cardId, }; } /** * 展会现场活动码与同场名片墙 */ @Provide() export class CardEventService extends BaseService { @InjectEntityModel(CardEventEntity) cardEventEntity: Repository; @InjectEntityModel(CardEventMemberEntity) cardEventMemberEntity: Repository; @InjectEntityModel(CardInfoEntity) cardInfoEntity: Repository; async resolveUserCard( userId: number, explicit?: number | null ): Promise { if (explicit != null && Number(explicit) > 0) { const card = await this.cardInfoEntity.findOne({ where: { id: Number(explicit) }, }); if (!card || Number(card.userId) !== Number(userId)) { throw new CoolCommException('名片不属于当前账号', 400); } return card; } const published = await this.cardInfoEntity.findOne({ where: { userId: Number(userId), status: 'published' }, }); if (published) { return published; } const anyCard = await this.cardInfoEntity.findOne({ where: { userId: Number(userId) }, }); if (!anyCard) { throw new CoolCommException('请先创建名片', 400); } return anyCard; } async joinEvent(input: JoinEventInput): Promise { if (!input?.userId) { throw new CoolCommException('请先登录', 401); } const code = normalizeEventCode(input.code); let event = await this.cardEventEntity.findOne({ where: { code } }); if (!event) { event = new CardEventEntity(); event.code = code; event.name = code; event.status = 'active'; event = await this.cardEventEntity.save(event); } const card = await this.resolveUserCard(input.userId, input.cardId); let member = await this.cardEventMemberEntity.findOne({ where: { eventId: Number(event.id), userId: Number(input.userId), }, }); if (!member) { member = new CardEventMemberEntity(); member.eventId = Number(event.id); member.userId = Number(input.userId); } member.cardId = Number(card.id); if (card.tenantId && !member.tenantId) { member.tenantId = Number(card.tenantId); } member = await this.cardEventMemberEntity.save(member); return { event: toEvent(event), member: toMember(member), }; } async currentEvent(userId: number): Promise { const members = await this.cardEventMemberEntity.find({ where: { userId: Number(userId) }, }); if (!members.length) { return null; } const latest = members[members.length - 1]; const event = await this.cardEventEntity.findOne({ where: { id: Number(latest.eventId) }, }); return event ? toEvent(event) : null; } async pageEventFloor( query: PageEventFloorQuery ): Promise<{ list: CardContact[]; pagination: { page: number; size: number; total: number }; }> { const page = query.page && query.page > 0 ? Math.floor(query.page) : 1; const size = query.size && query.size > 0 ? Math.floor(query.size) : 20; const members = await this.cardEventMemberEntity.find({ where: { userId: Number(query.userId) }, }); if (!members.length) { return { list: [], pagination: { page, size, total: 0 }, }; } const latest = members[members.length - 1]; const peers = await this.cardEventMemberEntity.find({ where: { eventId: Number(latest.eventId) }, }); const keyword = query.keyWord ? String(query.keyWord).trim().toLowerCase() : ''; const hydrated: CardContact[] = []; for (const peer of peers) { if (Number(peer.userId) === Number(query.userId)) { continue; } const card = await this.cardInfoEntity.findOne({ where: { id: Number(peer.cardId) }, }); if (!card || card.status !== 'published') { continue; } if (keyword) { const hay = `${card.name || ''} ${card.title || ''} ${ card.company || '' }`.toLowerCase(); if (!hay.includes(keyword)) { continue; } } hydrated.push({ ownerUserId: Number(query.userId), peerCardId: Number(card.id), source: 'event', peerName: card.name, peerTitle: card.title, peerCompany: card.company, peerAvatarUrl: card.avatarUrl, peerSlug: card.slug, }); } const total = hydrated.length; const start = (page - 1) * size; return { list: hydrated.slice(start, start + size), pagination: { page, size, total }, }; } }