import { Injectable } from '@angular/core'; import { FmodeParse, FmodeObject } from 'fmode-ng/parse'; import * as ww from '@wecom/jssdk'; import { WxworkCorp } from 'fmode-ng/core'; const Parse = FmodeParse.with('nova'); export interface WxworkCurrentChat { type: 'chatId' | 'userId'; id?: string; contact?: any; follow_user?: any; group?: any; } /** * 企微SDK服务 * 封装企业微信JSSDK功能 */ @Injectable({ providedIn: 'root' }) export class WxworkSDKService { // 企业配置映射 private companyMap: any = { 'cDL6R1hgSi': { // 映三色 corpResId: 'SpL6gyD1Gu' } }; // 应用套件映射 private suiteMap: any = { 'crm': { suiteId: 'dk2559ba758f33d8f5' }, 'project': { // 🔥 添加project应用的配置(使用相同的suiteId) suiteId: 'dk2559ba758f33d8f5' } }; cid: string = ''; appId: string = ''; corpId: string = ''; wecorp: WxworkCorp | null = null; ww = ww; registerUrl: string = ''; constructor() {} /** * 初始化SDK */ async initialize(cid: string, appId: string): Promise { this.cid = cid; this.appId = appId; this.wecorp = new WxworkCorp(cid); await this.registerCorpWithSuite(); } /** * 注册企业微信JSAPI */ async registerCorpWithSuite(apiList?: string[]): Promise { console.log('🔍 [registerCorpWithSuite] 开始注册JSSDK...'); console.log('🔍 [registerCorpWithSuite] 平台检测:', this.platform()); if (this.platform() !== 'wxwork') { console.warn('⚠️ [registerCorpWithSuite] 非企业微信环境,跳过注册'); return false; } // 如果URL未变化且已注册,直接返回 if (!apiList?.length && this.registerUrl === location.href) { console.log('✅ [registerCorpWithSuite] URL未变化,使用缓存的注册状态'); return true; } apiList = apiList || this.getDefaultApiList(); console.log('🔍 [registerCorpWithSuite] API列表:', apiList); try { console.log('🔍 [registerCorpWithSuite] 获取企业配置,CID:', this.cid); const corpConfig = await this.getCorpByCid(this.cid); console.log('🔍 [registerCorpWithSuite] 企业配置:', corpConfig); const suiteId = this.suiteMap[this.appId]?.suiteId; console.log('🔍 [registerCorpWithSuite] 套件ID:', suiteId); const now = new Date(); return new Promise((resolve) => { console.log('🔍 [registerCorpWithSuite] 调用ww.register...'); console.log('🔍 [registerCorpWithSuite] corpId:', corpConfig.corpId); console.log('🔍 [registerCorpWithSuite] agentId:', corpConfig.agentId); console.log('🔍 [registerCorpWithSuite] suiteId:', suiteId); console.log('🔍 [registerCorpWithSuite] url:', location.href); // 🔥 添加15秒超时机制 const timeout = setTimeout(() => { console.warn('⚠️ [registerCorpWithSuite] 注册超时(15秒),可能原因:'); console.warn(' 1. 回调函数未被触发'); console.warn(' 2. 企业微信JSSDK加载失败'); console.warn(' 3. 不在正确的群聊会话中'); console.warn(' 4. 应用权限配置错误'); resolve(false); }, 15000); ww.register({ corpId: corpConfig.corpId, suiteId: suiteId, agentId: corpConfig.agentId, jsApiList: apiList!, getAgentConfigSignature: async () => { console.log('🔍 [registerCorpWithSuite] 获取签名...'); const jsapiTicket = await this.wecorp!.ticket.get(); console.log('🔍 [registerCorpWithSuite] Ticket:', jsapiTicket?.substring(0, 20) + '...'); const signature = ww.getSignature({ ticket: jsapiTicket, nonceStr: '666', timestamp: (now.getTime() / 1000).toFixed(0), url: location.href }); console.log('🔍 [registerCorpWithSuite] 签名生成完成:', { nonceStr: '666', timestamp: (now.getTime() / 1000).toFixed(0), url: location.href }); return signature; }, onAgentConfigSuccess: () => { clearTimeout(timeout); console.log('✅ [registerCorpWithSuite] AgentConfig注册成功!'); this.registerUrl = location.href; resolve(true); }, onAgentConfigFail: (err: any) => { clearTimeout(timeout); console.error('❌ [registerCorpWithSuite] AgentConfig注册失败:', err); console.error('❌ 错误详情:', JSON.stringify(err, null, 2)); console.error('❌ 请检查:'); console.error(' 1. agentId是否正确'); console.error(' 2. 应用是否已发布'); console.error(' 3. jsapi_ticket是否有效'); resolve(false); }, onConfigFail: (err: any) => { clearTimeout(timeout); console.error('❌ [registerCorpWithSuite] Config注册失败:', err); console.error('❌ 错误详情:', JSON.stringify(err, null, 2)); console.error('❌ 请检查:'); console.error(' 1. corpId是否正确'); console.error(' 2. suiteId是否正确'); console.error(' 3. 企业是否已授权'); resolve(false); } }); }); } catch (error) { console.error('❌ [registerCorpWithSuite] 注册过程出错:', error); return false; } } /** * 获取当前聊天对象 */ async getCurrentChatObject(): Promise<{ GroupChat?: FmodeObject; Contact?: FmodeObject; currentChat: WxworkCurrentChat | null; }> { const currentChat = await this.getCurrentChat(); if (!currentChat) { return { currentChat: null }; } let GroupChat: FmodeObject | undefined; let Contact: FmodeObject | undefined; try { if (currentChat.type === 'chatId' && currentChat.group) { GroupChat = await this.syncGroupChat(currentChat.group); } else if (currentChat.type === 'userId' && currentChat.id) { const contactInfo = await this.wecorp!.externalContact.get(currentChat.id); Contact = await this.syncContact(contactInfo); } } catch (error) { console.error('getCurrentChatObject error:', error); } return { GroupChat, Contact, currentChat }; } /** * 获取当前聊天场景 */ async getCurrentChat(): Promise { const isRegister = await this.registerCorpWithSuite(); if (!isRegister) return null; try { const context = await ww.getContext(); const entry = context?.entry; let type: 'chatId' | 'userId'; let id: string | undefined; let contact: any; let chat: any; if (entry === 'group_chat_tools') { type = 'chatId'; id = (await ww.getCurExternalChat())?.chatId; chat = await this.wecorp!.externalContact.groupChat.get(id!); } else if (entry === 'contact_profile' || entry === 'single_chat_tools') { type = 'userId'; id = (await ww.getCurExternalContact())?.userId; contact = await this.wecorp!.externalContact.get(id!); } else { return null; } return { type, id, group: chat?.group_chat, contact: contact?.external_contact, follow_user: contact?.follow_user }; } catch (error) { console.error('getCurrentChat error:', error); return null; } } /** * 获取当前用户信息 */ async getCurrentUser(): Promise { const userInfo = await this.getUserinfo(); if (!userInfo) return null; return await this.getContactOrProfile(userInfo); } /** * 获取用户信息 */ async getUserinfo(code?: string): Promise { // 优先检查缓存 if (!code) { const userInfoStr = localStorage.getItem(`${this.cid}/USERINFO`); if (userInfoStr) { const userInfo = JSON.parse(userInfoStr); userInfo.cid = this.cid; return userInfo; } } // 从URL获取code const url = new URL(location.href); code = url.searchParams.get('code') || code; if (!code) return null; const result = await this.wecorp!.auth.getuserinfo(code); if (result?.errcode) { console.error(result?.errmsg); return null; } // 补全外部用户信息 if (result?.external_userid) { const euser = await this.wecorp!.externalContact.get(result.external_userid); if (euser?.external_contact) { Object.assign(result, euser.external_contact); } } result.cid = this.cid; // 缓存用户信息 localStorage.setItem(`${this.cid}/USERINFO`, JSON.stringify(result)); return result; } /** * 同步群聊信息 */ async syncGroupChat(groupInfo: any): Promise { let query = new Parse.Query('GroupChat'); query.equalTo('chat_id', groupInfo?.chat_id); let group = await query.first(); if (!group?.id) { group = new Parse.Object('GroupChat'); } // 生成入群方式 if (!group?.get('joinUrl')) { const config_id1 = (await this.wecorp!.externalContact.groupChat.addJoinWay({ scene: 1, chat_id_list: [groupInfo.chat_id] }))?.config_id; const joinUrl = (await this.wecorp!.externalContact.groupChat.getJoinWay(config_id1))?.join_way; group.set('joinUrl', joinUrl); } if (!group?.get('joinQrcode')) { const config_id2 = (await this.wecorp!.externalContact.groupChat.addJoinWay({ scene: 2, chat_id_list: [groupInfo.chat_id] }))?.config_id; const joinQrcode = (await this.wecorp!.externalContact.groupChat.getJoinWay(config_id2))?.join_way; group.set('joinQrcode', joinQrcode); } // 更新群聊数据 let needSave = false; if (group.get('chat_id') !== groupInfo.chat_id) needSave = true; if (group.get('name') !== groupInfo.name) needSave = true; if (group.get('owner') !== groupInfo.owner) needSave = true; if (group.get('notice') !== groupInfo.notice) needSave = true; if (group.get('member_version') !== groupInfo.member_version) { needSave = true; group.set('member_list', groupInfo.member_list); group.set('member_version', groupInfo.member_version); } group.set({ chat_id: groupInfo.chat_id, name: groupInfo.name, owner: groupInfo.owner, notice: groupInfo.notice }); if (this.cid) { group.set('company', { __type: 'Pointer', className: 'Company', objectId: this.cid }); } if (needSave) { group = await group.save(); } return group; } /** * 同步联系人信息 */ async syncContact(contactInfo: any): Promise { const externalContact = contactInfo.external_contact || contactInfo; const externalUserId = externalContact.external_userid; let query = new Parse.Query('ContactInfo'); query.equalTo('external_userid', externalUserId); const Company = new Parse.Object('Company'); Company.id = this.cid; query.equalTo('company', Company); let contact = await query.first(); if (!contact?.id) { contact = new Parse.Object('ContactInfo'); if (Company?.id) { contact.set('company', Company.toPointer()); } } const name = externalContact.name || ''; const mobile = externalContact.mobile || ''; const data: any = { ...externalContact, follow_user: contactInfo.follow_user || externalContact.follow_user || [] }; let needSave = false; if (contact.get('external_userid') !== externalUserId) needSave = true; if (contact.get('name') !== name && name) needSave = true; if (contact.get('mobile') !== mobile && mobile) needSave = true; const oldData = contact.get('data'); if (JSON.stringify(oldData) !== JSON.stringify(data)) needSave = true; contact.set('external_userid', externalUserId); contact.set('name', name); contact.set('mobile', mobile); contact.set('data', data); if (needSave) { contact = await contact.save(); } return contact; } /** * 获取Profile或UserSocial */ async getContactOrProfile(userInfo: any): Promise { let UserType: string; if (userInfo.openid || userInfo.external_userid) { UserType = 'UserSocial'; } else if (userInfo.userid) { UserType = 'Profile'; } else { throw new Error('Invalid user info'); } // 构建查询条件 const userCondition: any[] = []; const prefix = UserType === 'UserSocial' ? 'data.' : ''; if (userInfo.openid) userCondition.push({ [`${prefix}openid`]: { $regex: userInfo.openid } }); if (userInfo.userid) userCondition.push({ [`${prefix}userid`]: { $regex: userInfo.userid } }); if (userInfo.mobile) userCondition.push({ [`${prefix}mobile`]: { $regex: userInfo.mobile } }); if (userInfo.email) userCondition.push({ [`${prefix}email`]: { $regex: userInfo.email } }); if (userInfo.external_userid) { userCondition.push({ [`${prefix}external_userid`]: { $regex: userInfo.external_userid } }); } const query = Parse.Query.fromJSON(UserType, { where: { $or: userCondition } }); query.equalTo('company', this.cid); let thisUser = await query.first(); if (!thisUser?.id) { thisUser = new Parse.Object(UserType); } // 关联当前登录用户 const current = Parse.User.current(); if (current?.id && !thisUser?.get('user')?.id) { thisUser.set('user', current.toPointer()); } return thisUser; } /** * 创建群聊 */ async createGroupChat(options: { groupName: string; userIds?: string[]; externalUserIds?: string[]; }): Promise { const isRegister = await this.registerCorpWithSuite(); if (!isRegister) return null; return new Promise((resolve, reject) => { ww.createCorpGroupChat({ groupName: options.groupName, userIds: options.userIds, externalUserIds: options.externalUserIds, success: (data: any) => { resolve(data); }, fail: (err: any) => { reject(err); } }); }); } /** * 添加成员到群聊 */ async addUserToGroup(options: { chatId: string; userIds?: string[]; externalUserIds?: string[]; }): Promise { const isRegister = await this.registerCorpWithSuite(); if (!isRegister) return null; return new Promise((resolve, reject) => { (ww as any).updateCorpGroupChat({ chatId: options.chatId, userIds: options.userIds, externalUserIds: options.externalUserIds, success: (data:any) => { resolve(data); }, fail: (err:any) => { reject(err); } }); }); } /** * 打开指定群聊 */ async openChat(chatId: string): Promise { const isRegister = await this.registerCorpWithSuite(); if (!isRegister) return; return new Promise((resolve, reject) => { ww.openEnterpriseChat({ externalUserIds: [], groupName: '', chatId: chatId, success: () => { resolve(); }, fail: (err: any) => { reject(err); } }); }); } /** * 选择企业联系人 */ async selectEnterpriseContact(options?: { mode?: 'single' | 'multi'; type?: Array<'department' | 'user'>; }): Promise { const isRegister = await this.registerCorpWithSuite(); if (!isRegister) return null; return new Promise((resolve, reject) => { (ww as any).selectEnterpriseContact({ fromDepartmentId: -1, mode: options?.mode || 'multi', type: options?.type || ['department', 'user'], success: (data:any) => { resolve(data); }, fail: (err:any) => { reject(err); } }); }); } /** * 获取企业配置 */ private async getCorpByCid(cid: string): Promise { if (this.corpId) return { corpId: this.corpId }; const query = new Parse.Query('CloudResource'); const res = await query.get(this.companyMap[cid]?.corpResId); const config: any = res.get('config'); return config; } /** * 判断平台 */ private platform(): string { const ua = navigator.userAgent.toLowerCase(); if (ua.indexOf('wxwork') > -1) return 'wxwork'; if (ua.indexOf('wechat') > -1) return 'wechat'; return 'h5'; } /** * 发送消息到当前聊天窗口 */ async sendChatMessage(options: { msgtype: 'text' | 'image' | 'news'; text?: { content: string }; news?: { link: string; title: string; desc: string; imgUrl: string }; }): Promise { console.log('🔍 [sendChatMessage] ========== 开始发送消息 =========='); console.log('🔍 [sendChatMessage] 消息类型:', options.msgtype); console.log('🔍 [sendChatMessage] 消息内容:', JSON.stringify(options, null, 2)); console.log('🔍 [sendChatMessage] 开始注册JSSDK...'); const isRegister = await this.registerCorpWithSuite(['sendChatMessage']); console.log('🔍 [sendChatMessage] JSSDK注册结果:', isRegister); if (!isRegister) { console.error('❌ [sendChatMessage] JSSDK注册失败'); console.error('❌ 可能原因:'); console.error(' 1. 企业微信配置错误(corpId, suiteId, agentId)'); console.error(' 2. ticket获取失败'); console.error(' 3. URL签名错误'); console.error(' 4. 网络问题'); throw new Error('JSSDK注册失败'); } console.log('🔍 [sendChatMessage] 调用ww.sendChatMessage...'); return new Promise((resolve, reject) => { ww.sendChatMessage({ ...options, success: (res: any) => { console.log('✅ [sendChatMessage] 消息发送成功!'); console.log('✅ [sendChatMessage] 响应数据:', res); resolve(); }, fail: (err: any) => { console.error('❌ [sendChatMessage] 消息发送失败!'); console.error('❌ [sendChatMessage] 错误详情:', err); console.error('❌ [sendChatMessage] 错误消息:', err?.errMsg); console.error('❌ [sendChatMessage] 错误代码:', err?.errCode); // 提供详细的错误提示 if (err?.errMsg?.includes('no permission')) { console.error('❌ 权限不足!请在企业微信管理后台开启sendChatMessage权限'); } else if (err?.errMsg?.includes('not in session')) { console.error('❌ 不在聊天会话中!请从群聊工具或客户资料入口打开应用'); } reject(err); } } as any); }); } /** * 获取默认API列表 */ private getDefaultApiList(): string[] { return [ 'getContext', 'getCurExternalChat', 'getCurExternalContact', 'createCorpGroupChat', 'updateCorpGroupChat', 'openEnterpriseChat', 'selectEnterpriseContact', 'checkJsApi', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getLocation', 'openLocation', 'scanQRCode', 'closeWindow', 'sendChatMessage' // 🔥 添加发送消息权限 ]; } }