From f7a0e9454c749471a300529d71cf61cd939251f5 Mon Sep 17 00:00:00 2001 From: huxuejian Date: Tue, 14 Apr 2026 16:17:40 +0800 Subject: [PATCH 1/4] =?UTF-8?q?im=E7=99=BB=E5=BD=95=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=9C=BA=E6=9E=84id=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 2 +- pages/home/home.vue | 13 ++++---- pages/message/message.vue | 66 +++++++++++++++------------------------ store/account.js | 44 +++++++++++++++++++++----- 4 files changed, 71 insertions(+), 54 deletions(-) diff --git a/App.vue b/App.vue index 326d821..d9ca4e1 100644 --- a/App.vue +++ b/App.vue @@ -8,7 +8,7 @@ export default { console.log("App Launch: "); // 获取 openId 并初始化 IM - await this.initIMOnLaunch(); + // await this.initIMOnLaunch(); }, onShow: function () { const db = dbStore(); diff --git a/pages/home/home.vue b/pages/home/home.vue index 6315bfe..5c43a19 100644 --- a/pages/home/home.vue +++ b/pages/home/home.vue @@ -45,7 +45,7 @@ import pageLoading from "./loading.vue"; // const { useLoad, useShow } = useGuard(); const { account } = storeToRefs(useAccount()); -const { login } = useAccount(); +const { login, getTeams } = useAccount(); const team = ref(null); const teams = ref([]); @@ -77,10 +77,9 @@ async function changeTeam({ teamId, corpId, corpName }) { } } -async function getTeams(inviteTeamId = '') { +async function getMatchTeams(inviteTeamId = '') { loading.value = true; - const res = await api('getWxappRelateTeams', { openid: account.value.openid }); - teams.value = res && Array.isArray(res.data) ? res.data : []; + teams.value = await getTeams(); const matchTeamId = inviteTeamId || (team.value ? team.value.teamId : ''); const validTeam = teams.value.find(i => i.teamId && i.teamId === matchTeamId); const firstTeam = teams.value[0] @@ -110,7 +109,7 @@ onShow(async () => { corpUserIds.value[inviteTeam.teamId] = inviteTeam.corpUserId; } if (account.value && account.value.openid) { - getTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : ''); + getMatchTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : ''); } else { teams.value = []; } @@ -124,11 +123,13 @@ onShow(async () => { watch(account, (n, o) => { if (n && !o) { - getTeams(); + getMatchTeams(); } else if (!n && o) { teams.value = []; } }) + + diff --git a/utils/api.js b/utils/api.js index 1798a7c..210f151 100644 --- a/utils/api.js +++ b/utils/api.js @@ -72,7 +72,9 @@ const urlsConfig = { getGroupListByGroupId: "getGroupListByGroupId", createConsultGroup: "createConsultGroup", cancelConsultApplication: "cancelConsultApplication", - getGroupList: "getGroupList" + getGroupList: "getGroupList", + saveConversationSubscribeResult: "saveConversationSubscribeResult", + sendConversationSubscribeEvent: "sendConversationSubscribeEvent" }, survery: { getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList', diff --git a/utils/subscribe-message-config.js b/utils/subscribe-message-config.js new file mode 100644 index 0000000..df935c9 --- /dev/null +++ b/utils/subscribe-message-config.js @@ -0,0 +1,64 @@ +const env = __VITE_ENV__; + +export const SUBSCRIBE_MESSAGE_ROLE = { + PATIENT: "patient", + DOCTOR: "doctor", +}; + +export const SUBSCRIBE_MESSAGE_SCENE = { + DEFAULT: "default", + LIST: "list", + CHAT: "chat", +}; + +export const SUBSCRIBE_MESSAGE_EVENT = { + PATIENT_CONSULT_APPLY: "patient_consult_apply", + PATIENT_CHAT_MESSAGE: "patient_chat_message", + DOCTOR_ACCEPT: "doctor_accept", + DOCTOR_REJECT: "doctor_reject", + DOCTOR_CHAT_MESSAGE: "doctor_chat_message", +}; + +export const SUBSCRIBE_MESSAGE_TEMPLATES = { + consultationReply: { + code: "consultationReply", + role: SUBSCRIBE_MESSAGE_ROLE.PATIENT, + id: + env.MP_SUBSCRIBE_TEMPLATE_CONSULT_REPLY || + "VF9AC-7Rr3E1drbxBCrxbC-rLTnidmlNXopKReSAd_w", + name: "咨询回复通知", + events: [ + SUBSCRIBE_MESSAGE_EVENT.DOCTOR_ACCEPT, + SUBSCRIBE_MESSAGE_EVENT.DOCTOR_REJECT, + SUBSCRIBE_MESSAGE_EVENT.DOCTOR_CHAT_MESSAGE, + ], + fields: ["患者姓名", "回复时间", "回复者", "所属机构"], + }, +}; + +export const SUBSCRIBE_MESSAGE_SCENE_TEMPLATE_MAP = { + [SUBSCRIBE_MESSAGE_ROLE.PATIENT]: { + [SUBSCRIBE_MESSAGE_SCENE.DEFAULT]: ["consultationReply"], + [SUBSCRIBE_MESSAGE_SCENE.LIST]: ["consultationReply"], + [SUBSCRIBE_MESSAGE_SCENE.CHAT]: ["consultationReply"], + }, +}; + +export function resolveSubscribeTemplates({ + role, + scene = SUBSCRIBE_MESSAGE_SCENE.DEFAULT, +} = {}) { + const roleMap = SUBSCRIBE_MESSAGE_SCENE_TEMPLATE_MAP[role] || {}; + const keys = + roleMap[scene] || roleMap[SUBSCRIBE_MESSAGE_SCENE.DEFAULT] || []; + const seen = new Set(); + + return keys + .map((key) => SUBSCRIBE_MESSAGE_TEMPLATES[key]) + .filter((item) => item && item.id) + .filter((item) => { + if (seen.has(item.id)) return false; + seen.add(item.id); + return true; + }); +} diff --git a/utils/subscribe-message.js b/utils/subscribe-message.js new file mode 100644 index 0000000..8309fd8 --- /dev/null +++ b/utils/subscribe-message.js @@ -0,0 +1,194 @@ +import api from "@/utils/api"; +import { toast } from "@/utils/widget"; +import { resolveSubscribeTemplates } from "./subscribe-message-config"; + +const SUBSCRIBE_ACCEPT_STATUS = "accept"; +const SUBSCRIBE_REJECT_STATUS = "reject"; +const SUBSCRIBE_BAN_STATUS = "ban"; +const SUBSCRIBE_FILTER_STATUS = "filter"; +const SUBSCRIBE_CANCEL_STATUS = "cancel"; +const SUBSCRIBE_FAILED_STATUS = "failed"; + +function canUseSubscribeMessage() { + return ( + typeof wx !== "undefined" && + typeof wx.requestSubscribeMessage === "function" + ); +} + +function requestSubscribeMessage(tmplIds = []) { + return new Promise((resolve) => { + wx.requestSubscribeMessage({ + tmplIds, + success(res) { + resolve({ ok: true, res }); + }, + fail(err) { + resolve({ ok: false, err }); + }, + }); + }); +} + +function normalizeFailStatus(err = {}) { + const errCode = Number(err.errCode || 0); + const errMsg = String(err.errMsg || "").toLowerCase(); + + if (errCode === 20004 || errMsg.includes("main switch")) { + return SUBSCRIBE_BAN_STATUS; + } + if (errCode === 20005 || errMsg.includes("ban")) { + return SUBSCRIBE_BAN_STATUS; + } + if (errMsg.includes("filter")) { + return SUBSCRIBE_FILTER_STATUS; + } + if (errMsg.includes("cancel")) { + return SUBSCRIBE_CANCEL_STATUS; + } + return SUBSCRIBE_FAILED_STATUS; +} + +function buildTemplateResultRecords(templates = [], requestResult = {}, context = {}) { + const requestedAt = Date.now(); + + if (requestResult.ok) { + const res = requestResult.res || {}; + return templates.map((template) => ({ + role: context.role || "", + scene: context.scene || "", + conversationId: context.conversationId || "", + groupId: context.groupId || "", + corpId: context.corpId || "", + teamId: context.teamId || "", + patientId: context.patientId || "", + doctorId: context.doctorId || "", + userId: context.userId || "", + openid: context.openid || "", + unionid: context.unionid || "", + templateId: template.id, + templateCode: template.code, + templateName: template.name, + eventTypes: template.events, + status: String(res[template.id] || SUBSCRIBE_FAILED_STATUS), + rawResult: res, + requestedAt, + extraData: context.extraData || {}, + })); + } + + const status = normalizeFailStatus(requestResult.err); + return templates.map((template) => ({ + role: context.role || "", + scene: context.scene || "", + conversationId: context.conversationId || "", + groupId: context.groupId || "", + corpId: context.corpId || "", + teamId: context.teamId || "", + patientId: context.patientId || "", + doctorId: context.doctorId || "", + userId: context.userId || "", + openid: context.openid || "", + unionid: context.unionid || "", + templateId: template.id, + templateCode: template.code, + templateName: template.name, + eventTypes: template.events, + status, + rawResult: requestResult.err || {}, + requestedAt, + extraData: context.extraData || {}, + })); +} + +function buildToastMessage(records = [], reportResult = { success: false }) { + const accepted = records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS); + + if (accepted && reportResult?.success === false) { + return reportResult?.message || "提醒开启失败,请稍后再试"; + } + + if (records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS)) { + return "会话消息提醒开启"; + } + if (records.some((item) => item.status === SUBSCRIBE_BAN_STATUS)) { + return "请先在微信设置中开启订阅消息提醒"; + } + if (records.some((item) => item.status === SUBSCRIBE_FILTER_STATUS)) { + return "当前提醒模板暂不可用"; + } + if (records.some((item) => item.status === SUBSCRIBE_REJECT_STATUS)) { + return "你已拒绝本次提醒订阅"; + } + if (records.some((item) => item.status === SUBSCRIBE_CANCEL_STATUS)) { + return "你已取消本次提醒订阅"; + } + return "提醒订阅请求失败,请稍后再试"; +} + +async function reportSubscribeResult(records = []) { + if (!records.length) return { success: false }; + try { + return await api( + "saveConversationSubscribeResult", + { + records, + }, + false + ); + } catch (error) { + console.error("保存订阅结果失败:", error); + return { success: false, message: error?.message || "保存失败" }; + } +} + +export async function requestConversationSubscribeMessage(context = {}) { + const templates = resolveSubscribeTemplates({ + role: context.role, + scene: context.scene, + }); + const requestTemplates = templates.slice(0, 1); + + if (!requestTemplates.length) { + await toast("暂未配置提醒模板"); + return { + success: false, + code: "template_missing", + records: [], + }; + } + + if (!canUseSubscribeMessage()) { + await toast("当前微信版本不支持订阅消息"); + return { + success: false, + code: "unsupported", + records: [], + }; + } + + const requestResult = await requestSubscribeMessage( + requestTemplates.map((item) => item.id) + ); + const records = buildTemplateResultRecords( + requestTemplates, + requestResult, + context + ); + + const reportResult = await reportSubscribeResult(records); + await toast(buildToastMessage(records, reportResult)); + + const subscribeSuccess = + records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS) && + reportResult?.success !== false; + + return { + success: subscribeSuccess, + reportResult, + records, + acceptedTemplateIds: records + .filter((item) => item.status === SUBSCRIBE_ACCEPT_STATUS) + .map((item) => item.templateId), + }; +} From be54f97babf1bc7a0a06ac88a4de77aca1c91c08 Mon Sep 17 00:00:00 2001 From: Jafeng <2998840497@qq.com> Date: Thu, 16 Apr 2026 17:32:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E6=8F=90=E9=86=92=E5=8A=9F=E8=83=BD=E5=92=8C=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/message/index.vue | 50 ++++++++++++++++++++++++++++---------- pages/message/message.vue | 29 ++++++++++++++++++++-- utils/api.js | 1 + utils/subscribe-message.js | 28 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/pages/message/index.vue b/pages/message/index.vue index 3c7b679..bced8f0 100644 --- a/pages/message/index.vue +++ b/pages/message/index.vue @@ -20,7 +20,11 @@ > {{ chatStatusInfo.badgeText }} - + 接收提醒 @@ -195,7 +199,10 @@ import ChatInput from "./components/chat-input.vue"; import SystemMessage from "./components/system-message.vue"; import ConsultCancel from "./components/consult-cancel.vue"; import ConsultApply from "./components/consult-apply.vue"; -import { requestConversationSubscribeMessage } from "@/utils/subscribe-message"; +import { + checkConversationSubscribeEntryVisible, + requestConversationSubscribeMessage, +} from "@/utils/subscribe-message"; import { SUBSCRIBE_MESSAGE_ROLE, SUBSCRIBE_MESSAGE_SCENE, @@ -203,8 +210,9 @@ import { const timChatManager = globalTimChatManager; -// corpId 从群组信息中获取 -const corpId = ref(""); +// corpId 从群组信息中获取 +const corpId = ref(""); +const showSubscribeEntry = ref(false); // 获取登录状态 const { account, openid, isIMInitialized } = storeToRefs(useAccountStore()); @@ -866,10 +874,11 @@ const handleScrollToUpper = async () => { }; // 页面显示 -onShow(() => { - if (!account.value || !openid.value) { - uni.redirectTo({ - url: "/pages/login/login", +onShow(() => { + loadSubscribeEntryState(); + if (!account.value || !openid.value) { + uni.redirectTo({ + url: "/pages/login/login", }); return; } @@ -915,8 +924,16 @@ onShow(() => { } startIMMonitoring(30000); - } -}); + } +}); + +watch( + () => corpId.value, + () => { + loadSubscribeEntryState(); + }, + { immediate: true } +); // 页面隐藏 onHide(() => { @@ -1090,9 +1107,16 @@ const handleSubscribeReminder = async () => { }, }); }; - -// 页面卸载 -onUnmounted(() => { + +const loadSubscribeEntryState = async () => { + showSubscribeEntry.value = await checkConversationSubscribeEntryVisible( + corpId.value || "", + true + ); +}; + +// 页面卸载 +onUnmounted(() => { clearMessageCache(); // 移除键盘监听 diff --git a/pages/message/message.vue b/pages/message/message.vue index 6633d88..f0389f6 100644 --- a/pages/message/message.vue +++ b/pages/message/message.vue @@ -52,7 +52,11 @@ - + @@ -68,7 +72,10 @@ import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.j import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js"; import useGroupAvatars from "./hooks/use-group-avatars.js"; import GroupAvatar from "@/components/group-avatar.vue"; -import { requestConversationSubscribeMessage } from "@/utils/subscribe-message"; +import { + checkConversationSubscribeEntryVisible, + requestConversationSubscribeMessage, +} from "@/utils/subscribe-message"; import { SUBSCRIBE_MESSAGE_ROLE, SUBSCRIBE_MESSAGE_SCENE, @@ -86,6 +93,7 @@ const loading = ref(false); const loadingMore = ref(false); const hasMore = ref(false); const refreshing = ref(false); +const showSubscribeEntry = ref(false); // 群聊头像管理 const { loadGroupAvatars, getAvatarList } = useGroupAvatars(); @@ -516,14 +524,31 @@ const handleSubscribeReminder = async () => { }); }; +const loadSubscribeEntryState = async () => { + const currentCorpId = teams.value.find((item) => item?.corpId)?.corpId || ""; + showSubscribeEntry.value = await checkConversationSubscribeEntryVisible( + currentCorpId, + true + ); +}; + // 页面显示 onShow(async () => { // 页面显示时刷新 tabBar 徽章 // if (globalUnreadListenerManager.isInitialized) { // await globalUnreadListenerManager.refreshBadge(); // } + await loadSubscribeEntryState(); }); +watch( + () => teams.value.map((item) => item?.corpId).join(","), + () => { + loadSubscribeEntryState(); + }, + { immediate: true } +); + // 页面隐藏 onHide(() => { console.log("【消息列表页】页面隐藏"); diff --git a/utils/api.js b/utils/api.js index 210f151..96306a9 100644 --- a/utils/api.js +++ b/utils/api.js @@ -73,6 +73,7 @@ const urlsConfig = { createConsultGroup: "createConsultGroup", cancelConsultApplication: "cancelConsultApplication", getGroupList: "getGroupList", + getConversationSubscribeConfig: "getConversationSubscribeConfig", saveConversationSubscribeResult: "saveConversationSubscribeResult", sendConversationSubscribeEvent: "sendConversationSubscribeEvent" }, diff --git a/utils/subscribe-message.js b/utils/subscribe-message.js index 8309fd8..f3091e1 100644 --- a/utils/subscribe-message.js +++ b/utils/subscribe-message.js @@ -8,6 +8,7 @@ const SUBSCRIBE_BAN_STATUS = "ban"; const SUBSCRIBE_FILTER_STATUS = "filter"; const SUBSCRIBE_CANCEL_STATUS = "cancel"; const SUBSCRIBE_FAILED_STATUS = "failed"; +const subscribeDisplayConfigCache = new Map(); function canUseSubscribeMessage() { return ( @@ -142,6 +143,33 @@ async function reportSubscribeResult(records = []) { } } +export async function checkConversationSubscribeEntryVisible( + corpId = "", + forceRefresh = false +) { + const normalizedCorpId = String(corpId || "").trim(); + if (!normalizedCorpId) return false; + + if (!forceRefresh && subscribeDisplayConfigCache.has(normalizedCorpId)) { + return subscribeDisplayConfigCache.get(normalizedCorpId); + } + + try { + const result = await api( + "getConversationSubscribeConfig", + { corpId: normalizedCorpId }, + false + ); + const enabled = !!result?.data?.enabled; + subscribeDisplayConfigCache.set(normalizedCorpId, enabled); + return enabled; + } catch (error) { + console.error("获取订阅提醒显示配置失败:", error); + subscribeDisplayConfigCache.set(normalizedCorpId, false); + return false; + } +} + export async function requestConversationSubscribeMessage(context = {}) { const templates = resolveSubscribeTemplates({ role: context.role,