Compare commits

..

5 Commits

Author SHA1 Message Date
huxuejian
e7ef2ac975 Merge remote-tracking branch 'origin/dev-订阅消息' into dev-2.3 2026-04-18 16:00:41 +08:00
be54f97bab feat: 添加订阅提醒功能和相关配置 2026-04-16 17:32:14 +08:00
3e09131356 feat: 添加订阅消息功能 2026-04-16 11:22:27 +08:00
huxuejian
266db3cfa4 Update edit-archive.vue 2026-04-15 13:14:41 +08:00
huxuejian
f7a0e9454c im登录根据机构id来 2026-04-14 16:17:40 +08:00
10 changed files with 556 additions and 97 deletions

View File

@ -8,7 +8,7 @@ export default {
console.log("App Launch: "); console.log("App Launch: ");
// openId IM // openId IM
await this.initIMOnLaunch(); // await this.initIMOnLaunch();
}, },
onShow: function () { onShow: function () {
const db = dbStore(); const db = dbStore();

View File

@ -190,6 +190,7 @@ async function init() {
await getCustomer(); await getCustomer();
} else { } else {
const res = await getArchives(); const res = await getArchives();
console.log('res:', res)
if (res.length > 0) { if (res.length > 0) {
visible.value = true; visible.value = true;
} }
@ -204,13 +205,9 @@ async function init() {
async function getArchives() { async function getArchives() {
const res = await api('getUnbindMiniAppCustomers', { corpId: corpId.value, mobile: account.value?.mobile || '' }); const res = await api('getUnbindMiniAppCustomers', { corpId: corpId.value, mobile: account.value?.mobile || '' });
if (res && res.success) { customers.value = res && Array.isArray(res.data) ? res.data : [];
corpName.value = res.corpName; corpName.value = res && res.corpName ? res.corpName : '';
customers.value = Array.isArray(res.data) ? res.data : []; return customers.value
} else {
// toast(res?.message || '');
}
return []
} }
async function getBaseForm() { async function getBaseForm() {

View File

@ -45,7 +45,7 @@ import pageLoading from "./loading.vue";
// const { useLoad, useShow } = useGuard(); // const { useLoad, useShow } = useGuard();
const { account } = storeToRefs(useAccount()); const { account } = storeToRefs(useAccount());
const { login } = useAccount(); const { login, getTeams } = useAccount();
const team = ref(null); const team = ref(null);
const teams = ref([]); const teams = ref([]);
@ -77,10 +77,9 @@ async function changeTeam({ teamId, corpId, corpName }) {
} }
} }
async function getTeams(inviteTeamId = '') { async function getMatchTeams(inviteTeamId = '') {
loading.value = true; loading.value = true;
const res = await api('getWxappRelateTeams', { openid: account.value.openid }); teams.value = await getTeams();
teams.value = res && Array.isArray(res.data) ? res.data : [];
const matchTeamId = inviteTeamId || (team.value ? team.value.teamId : ''); const matchTeamId = inviteTeamId || (team.value ? team.value.teamId : '');
const validTeam = teams.value.find(i => i.teamId && i.teamId === matchTeamId); const validTeam = teams.value.find(i => i.teamId && i.teamId === matchTeamId);
const firstTeam = teams.value[0] const firstTeam = teams.value[0]
@ -110,7 +109,7 @@ onShow(async () => {
corpUserIds.value[inviteTeam.teamId] = inviteTeam.corpUserId; corpUserIds.value[inviteTeam.teamId] = inviteTeam.corpUserId;
} }
if (account.value && account.value.openid) { if (account.value && account.value.openid) {
getTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : ''); getMatchTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : '');
} else { } else {
teams.value = []; teams.value = [];
} }
@ -124,11 +123,13 @@ onShow(async () => {
watch(account, (n, o) => { watch(account, (n, o) => {
if (n && !o) { if (n && !o) {
getTeams(); getMatchTeams();
} else if (!n && o) { } else if (!n && o) {
teams.value = []; teams.value = [];
} }
}) })
</script> </script>
<style scoped> <style scoped>
.home-container { .home-container {

View File

@ -37,6 +37,13 @@ $primary-color: #0877F1;
justify-content: space-between; justify-content: space-between;
} }
.header-actions {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.patient-basic-info { .patient-basic-info {
display: flex; display: flex;
align-items: center; align-items: center;
@ -84,6 +91,26 @@ $primary-color: #0877F1;
} }
} }
.remind-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 8rpx 20rpx;
border-radius: 24rpx;
border: 2rpx solid #3876f6;
background: #fff;
}
.remind-btn:active {
opacity: 0.85;
}
.remind-btn-text {
font-size: 24rpx;
color: #3876f6;
line-height: 1.4;
}
.badge-processing { .badge-processing {
background: #d1ecf1; background: #d1ecf1;

View File

@ -12,6 +12,7 @@
>{{ patientInfo.sex }} · {{ patientInfo.age }}</text >{{ patientInfo.sex }} · {{ patientInfo.age }}</text
> >
</view> </view>
<view class="header-actions">
<view <view
class="status-badge" class="status-badge"
:class="chatStatusInfo.badgeClass" :class="chatStatusInfo.badgeClass"
@ -19,6 +20,14 @@
> >
<text class="badge-text">{{ chatStatusInfo.badgeText }}</text> <text class="badge-text">{{ chatStatusInfo.badgeText }}</text>
</view> </view>
<view
v-if="showSubscribeEntry"
class="remind-btn"
@click="handleSubscribeReminder"
>
<text class="remind-btn-text">接收提醒</text>
</view>
</view>
</view> </view>
</view> </view>
@ -190,11 +199,20 @@ import ChatInput from "./components/chat-input.vue";
import SystemMessage from "./components/system-message.vue"; import SystemMessage from "./components/system-message.vue";
import ConsultCancel from "./components/consult-cancel.vue"; import ConsultCancel from "./components/consult-cancel.vue";
import ConsultApply from "./components/consult-apply.vue"; import ConsultApply from "./components/consult-apply.vue";
import {
checkConversationSubscribeEntryVisible,
requestConversationSubscribeMessage,
} from "@/utils/subscribe-message";
import {
SUBSCRIBE_MESSAGE_ROLE,
SUBSCRIBE_MESSAGE_SCENE,
} from "@/utils/subscribe-message-config";
const timChatManager = globalTimChatManager; const timChatManager = globalTimChatManager;
// corpId // corpId
const corpId = ref(""); const corpId = ref("");
const showSubscribeEntry = ref(false);
// //
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore()); const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
@ -857,6 +875,7 @@ const handleScrollToUpper = async () => {
// //
onShow(() => { onShow(() => {
loadSubscribeEntryState();
if (!account.value || !openid.value) { if (!account.value || !openid.value) {
uni.redirectTo({ uni.redirectTo({
url: "/pages/login/login", url: "/pages/login/login",
@ -908,6 +927,14 @@ onShow(() => {
} }
}); });
watch(
() => corpId.value,
() => {
loadSubscribeEntryState();
},
{ immediate: true }
);
// //
onHide(() => { onHide(() => {
stopIMMonitoring(); stopIMMonitoring();
@ -1063,6 +1090,31 @@ const handleApplyConsult = async () => {
} }
}; };
const handleSubscribeReminder = async () => {
await requestConversationSubscribeMessage({
role: SUBSCRIBE_MESSAGE_ROLE.PATIENT,
scene: SUBSCRIBE_MESSAGE_SCENE.CHAT,
conversationId: chatInfo.value.conversationID || "",
groupId: groupId.value || "",
corpId: corpId.value || "",
patientId: patientId.value || "",
userId: openid.value || account.value?.openid || "",
openid: openid.value || account.value?.openid || "",
unionid: account.value?.unionid || "",
extraData: {
orderStatus: orderStatus.value || "",
page: "pages/message/index",
},
});
};
const loadSubscribeEntryState = async () => {
showSubscribeEntry.value = await checkConversationSubscribeEntryVisible(
corpId.value || "",
true
);
};
// //
onUnmounted(() => { onUnmounted(() => {
clearMessageCache(); clearMessageCache();

View File

@ -1,35 +1,17 @@
<template> <template>
<view class="message-page"> <view class="message-page">
<!-- 消息列表 --> <!-- 消息列表 -->
<scroll-view <scroll-view class="message-list" scroll-y="true" refresher-enabled :refresher-triggered="refreshing"
class="message-list" @refresherrefresh="handleRefresh" @scrolltolower="handleLoadMore">
scroll-y="true"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
@scrolltolower="handleLoadMore"
>
<!-- 加载状态 --> <!-- 加载状态 -->
<view <view v-if="loading && conversationList.length === 0" class="loading-container">
v-if="loading && conversationList.length === 0"
class="loading-container"
>
<text class="loading-text">加载中...</text> <text class="loading-text">加载中...</text>
</view> </view>
<!-- 消息列表项 --> <!-- 消息列表项 -->
<view <view v-for="conversation in conversationList" :key="conversation.groupID || conversation.conversationID"
v-for="conversation in conversationList" class="message-item" @click="handleClickConversation(conversation)">
:key="conversation.groupID || conversation.conversationID"
class="message-item"
@click="handleClickConversation(conversation)"
>
<view class="avatar-container"> <view class="avatar-container">
<GroupAvatar <GroupAvatar :avatarList="getAvatarList(conversation.groupID)" :size="96" classType="square" />
:avatarList="getAvatarList(conversation.groupID)"
:size="96"
classType="square"
/>
<view v-if="conversation.unreadCount > 0" class="unread-badge"> <view v-if="conversation.unreadCount > 0" class="unread-badge">
<text class="unread-text">{{ <text class="unread-text">{{
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
@ -57,10 +39,7 @@
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view <view v-if="!loading && conversationList.length === 0" class="empty-container">
v-if="!loading && conversationList.length === 0"
class="empty-container"
>
<image class="empty-image" src="/static/empty.svg" mode="aspectFit" /> <image class="empty-image" src="/static/empty.svg" mode="aspectFit" />
<text class="empty-text">暂无消息</text> <text class="empty-text">暂无消息</text>
</view> </view>
@ -72,11 +51,19 @@
}}</text> }}</text>
</view> </view>
</scroll-view> </scroll-view>
<view
v-if="showSubscribeEntry"
class="subscribe-entry"
@click="handleSubscribeReminder"
>
<text class="subscribe-entry-text">接收提醒</text>
</view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { ref, computed, onUnmounted } from "vue"; import { ref, computed, onUnmounted, watch } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import useAccountStore from "@/store/account.js"; import useAccountStore from "@/store/account.js";
@ -85,9 +72,19 @@ import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.j
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js"; import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import useGroupAvatars from "./hooks/use-group-avatars.js"; import useGroupAvatars from "./hooks/use-group-avatars.js";
import GroupAvatar from "@/components/group-avatar.vue"; import GroupAvatar from "@/components/group-avatar.vue";
import {
checkConversationSubscribeEntryVisible,
requestConversationSubscribeMessage,
} from "@/utils/subscribe-message";
import {
SUBSCRIBE_MESSAGE_ROLE,
SUBSCRIBE_MESSAGE_SCENE,
} from "@/utils/subscribe-message-config";
// //
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore()); const { account, openid, isIMInitialized, hasImCorpId, teams } = storeToRefs(
useAccountStore()
);
const { initIMAfterLogin } = useAccountStore(); const { initIMAfterLogin } = useAccountStore();
// //
@ -96,6 +93,7 @@ const loading = ref(false);
const loadingMore = ref(false); const loadingMore = ref(false);
const hasMore = ref(false); const hasMore = ref(false);
const refreshing = ref(false); const refreshing = ref(false);
const showSubscribeEntry = ref(false);
// //
const { loadGroupAvatars, getAvatarList } = useGroupAvatars(); const { loadGroupAvatars, getAvatarList } = useGroupAvatars();
@ -479,6 +477,7 @@ const handleLoadMore = () => {
// //
const handleRefresh = async () => { const handleRefresh = async () => {
if (!hasImCorpId.value) return;
refreshing.value = true; refreshing.value = true;
try { try {
@ -489,7 +488,7 @@ const handleRefresh = async () => {
}; };
// //
onLoad( async() => { async function init() {
try { try {
// IM // IM
const imReady = await initIM(); const imReady = await initIM();
@ -503,7 +502,7 @@ onLoad( async() => {
} catch (error) { } catch (error) {
console.log("页面初始化异常:", error.message); console.log("页面初始化异常:", error.message);
} }
}); };
// //
const cleanMessageText = (text) => { const cleanMessageText = (text) => {
@ -511,14 +510,45 @@ const cleanMessageText = (text) => {
return text.replace(/[\r\n]+/g, " ").trim(); return text.replace(/[\r\n]+/g, " ").trim();
}; };
const handleSubscribeReminder = async () => {
await requestConversationSubscribeMessage({
role: SUBSCRIBE_MESSAGE_ROLE.PATIENT,
scene: SUBSCRIBE_MESSAGE_SCENE.LIST,
corpId: teams.value.find((item) => item?.corpId)?.corpId || "",
userId: openid.value || account.value?.openid || "",
openid: openid.value || account.value?.openid || "",
unionid: account.value?.unionid || "",
extraData: {
page: "pages/message/message",
},
});
};
const loadSubscribeEntryState = async () => {
const currentCorpId = teams.value.find((item) => item?.corpId)?.corpId || "";
showSubscribeEntry.value = await checkConversationSubscribeEntryVisible(
currentCorpId,
true
);
};
// //
onShow(async () => { onShow(async () => {
// tabBar // tabBar
// if (globalUnreadListenerManager.isInitialized) { // if (globalUnreadListenerManager.isInitialized) {
// await globalUnreadListenerManager.refreshBadge(); // await globalUnreadListenerManager.refreshBadge();
// } // }
await loadSubscribeEntryState();
}); });
watch(
() => teams.value.map((item) => item?.corpId).join(","),
() => {
loadSubscribeEntryState();
},
{ immediate: true }
);
// //
onHide(() => { onHide(() => {
console.log("【消息列表页】页面隐藏"); console.log("【消息列表页】页面隐藏");
@ -534,6 +564,11 @@ onHide(() => {
// //
}); });
watch(hasImCorpId, (n, o) => {
if (n && !o) {
init();
}
}, { immediate: true })
// //
onUnmounted(() => { onUnmounted(() => {
console.log("【消息列表页】页面卸载,清理回调"); console.log("【消息列表页】页面卸载,清理回调");
@ -732,4 +767,32 @@ onUnmounted(() => {
color: #999; color: #999;
padding-bottom: 10rpx; padding-bottom: 10rpx;
} }
.subscribe-entry {
position: fixed;
right: 32rpx;
bottom: 180rpx;
width: 116rpx;
height: 116rpx;
border-radius: 58rpx;
background: #fff;
border: 2rpx solid #3876f6;
box-shadow: 0 8rpx 24rpx rgba(56, 118, 246, 0.16);
display: flex;
align-items: center;
justify-content: center;
z-index: 20;
}
.subscribe-entry:active {
opacity: 0.85;
}
.subscribe-entry-text {
width: 56rpx;
font-size: 24rpx;
line-height: 1.4;
color: #3876f6;
text-align: center;
}
</style> </style>

View File

@ -1,4 +1,4 @@
import { ref, watch } from "vue"; import { ref, watch, computed } from "vue";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import api from '@/utils/api'; import api from '@/utils/api';
import { toast } from '@/utils/widget'; import { toast } from '@/utils/widget';
@ -13,6 +13,9 @@ export default defineStore("accountStore", () => {
const isIMInitialized = ref(false); const isIMInitialized = ref(false);
const openid = ref(""); const openid = ref("");
const externalUserId = ref(''); const externalUserId = ref('');
const teams = ref([]);
const hasImCorpId = computed(() => teams.value.some(i => i.corpId === 'wpLgjyawAA8N0gWmXgyJq8wpjGcOT7fg'));
const teamsPromise = ref(null);
async function login(phoneCode = '') { async function login(phoneCode = '') {
if (loading.value) return; if (loading.value) return;
@ -38,7 +41,7 @@ export default defineStore("accountStore", () => {
uni.setStorageSync('account', res.data); uni.setStorageSync('account', res.data);
uni.setStorageSync('openid', res.data.openid); uni.setStorageSync('openid', res.data.openid);
initIMAfterLogin(openid.value) // initIMAfterLogin(openid.value)
return res.data return res.data
} }
} }
@ -120,6 +123,21 @@ export default defineStore("accountStore", () => {
}); });
} }
async function searchTeams() {
const res = await api('getWxappRelateTeams', { openid: account.value.openid });
teams.value = res && Array.isArray(res.data) ? res.data : [];
return teams.value;
}
async function getTeams() {
if (!teamsPromise.value) {
teamsPromise.value = searchTeams();
}
await teamsPromise.value;
teamsPromise.value = null;
return teams.value;
}
async function getExternalUserId(corpId) { async function getExternalUserId(corpId) {
const unionid = account.value?.unionid; const unionid = account.value?.unionid;
const openid = account.value?.openid; const openid = account.value?.openid;
@ -130,5 +148,17 @@ export default defineStore("accountStore", () => {
} }
} }
return { account, login, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId } watch(hasImCorpId, n => {
if (n) {
initIMAfterLogin();
}
}, { immediate: true })
watch(openid, (n, o) => {
if (n && !o) {
getTeams();
}
}, { immediate: true })
return { account, teams, hasImCorpId, login, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId, getTeams }
}) })

View File

@ -72,7 +72,10 @@ const urlsConfig = {
getGroupListByGroupId: "getGroupListByGroupId", getGroupListByGroupId: "getGroupListByGroupId",
createConsultGroup: "createConsultGroup", createConsultGroup: "createConsultGroup",
cancelConsultApplication: "cancelConsultApplication", cancelConsultApplication: "cancelConsultApplication",
getGroupList: "getGroupList" getGroupList: "getGroupList",
getConversationSubscribeConfig: "getConversationSubscribeConfig",
saveConversationSubscribeResult: "saveConversationSubscribeResult",
sendConversationSubscribeEvent: "sendConversationSubscribeEvent"
}, },
survery: { survery: {
getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList', getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList',

View File

@ -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;
});
}

222
utils/subscribe-message.js Normal file
View File

@ -0,0 +1,222 @@
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";
const subscribeDisplayConfigCache = new Map();
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 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,
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),
};
}