This commit is contained in:
parent
b359070a0e
commit
681d2b5fe8
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"git.ignoreLimitWarning": true
|
"git.ignoreLimitWarning": true,
|
||||||
|
"kiro.chat.autoSummarize.threshold": 30
|
||||||
}
|
}
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
import request from '../utils/request'
|
import request from '../utils/request'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
@ -141,6 +142,18 @@ async function fetchList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteChat(row) {
|
||||||
|
try {
|
||||||
|
const groupId = row.imGroupId || `order_${row.id}`
|
||||||
|
await request.delete(`/admin/chat-list/${groupId}`)
|
||||||
|
ElMessage.success('已删除')
|
||||||
|
// 从列表中移除
|
||||||
|
list.value = list.value.filter(item => item.id !== row.id)
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function viewChat(row) {
|
async function viewChat(row) {
|
||||||
chatDialogTitle.value = `聊天记录 - ${row.orderNo} (${row.ownerNickname} ↔ ${row.runnerNickname})`
|
chatDialogTitle.value = `聊天记录 - ${row.orderNo} (${row.ownerNickname} ↔ ${row.runnerNickname})`
|
||||||
chatDialogVisible.value = true
|
chatDialogVisible.value = true
|
||||||
|
|
@ -148,14 +161,9 @@ async function viewChat(row) {
|
||||||
chatMessages.value = []
|
chatMessages.value = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {}
|
// 统一用群ID获取聊天记录
|
||||||
// 优先用群ID(群聊模式)
|
const groupId = row.imGroupId || `order_${row.id}`
|
||||||
if (row.imGroupId) {
|
const params = { groupId }
|
||||||
params.groupId = row.imGroupId
|
|
||||||
} else {
|
|
||||||
// 兼容旧数据
|
|
||||||
params.groupId = `order_${row.id}`
|
|
||||||
}
|
|
||||||
const res = await request.get('/admin/chat-messages', { params })
|
const res = await request.get('/admin/chat-messages', { params })
|
||||||
chatMessages.value = parseIMMessages(res, row)
|
chatMessages.value = parseIMMessages(res, row)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -190,7 +198,7 @@ function parseIMMessages(imResponse, orderInfo) {
|
||||||
const showTime = timestamp - lastTime > 300000
|
const showTime = timestamp - lastTime > 300000
|
||||||
let timeLabel = ''
|
let timeLabel = ''
|
||||||
if (showTime && timestamp) {
|
if (showTime && timestamp) {
|
||||||
timeLabel = formatTime(timestamp / 1000 > 1e12 ? timestamp : timestamp * 1000)
|
timeLabel = formatTime(new Date(timestamp))
|
||||||
lastTime = timestamp
|
lastTime = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,17 @@
|
||||||
<el-radio-group v-model="statusFilter" @change="loadData">
|
<el-radio-group v-model="statusFilter" @change="loadData">
|
||||||
<el-radio-button label="">全部</el-radio-button>
|
<el-radio-button label="">全部</el-radio-button>
|
||||||
<el-radio-button label="Pending">待处理</el-radio-button>
|
<el-radio-button label="Pending">待处理</el-radio-button>
|
||||||
<el-radio-button label="Processing">处理中</el-radio-button>
|
|
||||||
<el-radio-button label="Completed">已完成</el-radio-button>
|
<el-radio-button label="Completed">已完成</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="list" stripe v-loading="loading" style="width: 100%">
|
<el-table :data="list" stripe v-loading="loading" style="width: 100%">
|
||||||
<el-table-column prop="id" label="ID" width="70" />
|
<el-table-column prop="id" label="ID" width="70" />
|
||||||
<el-table-column prop="userNickname" label="用户" min-width="100" show-overflow-tooltip />
|
<el-table-column prop="userNickname" label="用户" min-width="100" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.userNickname }} <span style="color:#999;font-size:11px;">{{ row.userUid }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="amount" label="金额" width="100">
|
<el-table-column prop="amount" label="金额" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span style="color: #e64340; font-weight: bold">¥{{ row.amount }}</span>
|
<span style="color: #e64340; font-weight: bold">¥{{ row.amount }}</span>
|
||||||
|
|
@ -25,13 +28,6 @@
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款码" width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-image v-if="row.qrCodeImage" :src="row.qrCodeImage" :preview-src-list="[row.qrCodeImage]"
|
|
||||||
style="width: 40px; height: 40px" fit="cover" preview-teleported />
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="status" label="状态" width="100">
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="statusTagType(row.status)" round size="small">{{ statusLabel(row.status) }}</el-tag>
|
<el-tag :type="statusTagType(row.status)" round size="small">{{ statusLabel(row.status) }}</el-tag>
|
||||||
|
|
@ -42,12 +38,7 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="220" fixed="right">
|
<el-table-column label="操作" width="220" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<template v-if="row.status === 'Pending'">
|
<template v-if="row.status === 'Pending' || row.status === 'Processing'">
|
||||||
<el-button type="warning" size="small" plain @click="handleAction(row, 'processing')">处理中</el-button>
|
|
||||||
<el-button type="success" size="small" plain @click="handleAction(row, 'approve')">通过</el-button>
|
|
||||||
<el-button type="danger" size="small" plain @click="handleAction(row, 'reject')">拒绝</el-button>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="row.status === 'Processing'">
|
|
||||||
<el-button type="success" size="small" plain @click="handleAction(row, 'approve')">通过</el-button>
|
<el-button type="success" size="small" plain @click="handleAction(row, 'approve')">通过</el-button>
|
||||||
<el-button type="danger" size="small" plain @click="handleAction(row, 'reject')">拒绝</el-button>
|
<el-button type="danger" size="small" plain @click="handleAction(row, 'reject')">拒绝</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,53 @@
|
||||||
<script>
|
<script>
|
||||||
import { initIM } from './utils/im'
|
import { initIM, getConversationList } from './utils/im'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
globalData: {
|
||||||
|
badgeTimer: null
|
||||||
|
},
|
||||||
onLaunch: function() {
|
onLaunch: function() {
|
||||||
// 检查本地登录凭证,过期则静默清除,不跳转登录页
|
|
||||||
const token = uni.getStorageSync('token')
|
const token = uni.getStorageSync('token')
|
||||||
if (!token) return
|
if (!token) return
|
||||||
|
|
||||||
try {
|
|
||||||
const parts = token.split('.')
|
|
||||||
if (parts.length === 3) {
|
|
||||||
const payload = JSON.parse(atob(parts[1]))
|
|
||||||
if (payload.exp && payload.exp * 1000 < Date.now()) {
|
|
||||||
// token 已过期,静默清除
|
|
||||||
uni.removeStorageSync('token')
|
|
||||||
uni.removeStorageSync('userInfo')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// 解析失败,清除无效 token
|
|
||||||
uni.removeStorageSync('token')
|
|
||||||
uni.removeStorageSync('userInfo')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// token 有效,后台初始化 IM
|
|
||||||
initIM().catch(e => {
|
initIM().catch(e => {
|
||||||
console.log('[App] IM 初始化失败(非阻塞):', e.message)
|
console.log('[App] IM 初始化失败(非阻塞):', e.message)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onShow: function() {},
|
onShow: function() {
|
||||||
onHide: function() {}
|
this.updateTabBarBadge()
|
||||||
|
// 全局定时刷新 tabBar 未读数(每10秒)
|
||||||
|
if (!this.globalData.badgeTimer) {
|
||||||
|
this.globalData.badgeTimer = setInterval(() => {
|
||||||
|
this.updateTabBarBadge()
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHide: function() {
|
||||||
|
if (this.globalData.badgeTimer) {
|
||||||
|
clearInterval(this.globalData.badgeTimer)
|
||||||
|
this.globalData.badgeTimer = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async updateTabBarBadge() {
|
||||||
|
try {
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (!token) return
|
||||||
|
// 仅在 TabBar 页面才操作角标,避免非 TabBar 页面报错
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1]
|
||||||
|
const tabBarPaths = ['pages/index/index', 'pages/order-hall/order-hall', 'pages/message/message', 'pages/mine/mine']
|
||||||
|
if (!currentPage || !tabBarPaths.includes(currentPage.route)) return
|
||||||
|
const convList = await getConversationList()
|
||||||
|
const total = convList.reduce((sum, c) => sum + (c.unreadCount || 0), 0)
|
||||||
|
if (total > 0) {
|
||||||
|
uni.setTabBarBadge({ index: 2, text: total > 99 ? '99+' : String(total) })
|
||||||
|
} else {
|
||||||
|
uni.removeTabBarBadge({ index: 2 })
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,17 @@
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
return this.orderInfo.ownerId === userStore.userId
|
return this.orderInfo.ownerId === userStore.userId
|
||||||
},
|
},
|
||||||
|
/** 构建 IM userId -> 头像 的映射 */
|
||||||
|
avatarMap() {
|
||||||
|
const map = {}
|
||||||
|
if (this.orderInfo.ownerId) {
|
||||||
|
map[`user_${this.orderInfo.ownerId}`] = this.orderInfo.ownerAvatar || ''
|
||||||
|
}
|
||||||
|
if (this.orderInfo.runnerId) {
|
||||||
|
map[`user_${this.orderInfo.runnerId}`] = this.orderInfo.runnerAvatar || ''
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
},
|
||||||
showGoodsPriceBtn() {
|
showGoodsPriceBtn() {
|
||||||
const type = this.orderInfo.orderType
|
const type = this.orderInfo.orderType
|
||||||
return type === 'Purchase' || type === 'Food'
|
return type === 'Purchase' || type === 'Food'
|
||||||
|
|
@ -329,7 +340,7 @@
|
||||||
for (const msg of msgList) {
|
for (const msg of msgList) {
|
||||||
// 只处理当前群的消息
|
// 只处理当前群的消息
|
||||||
if (msg.conversationID === `GROUP${this.imGroupId}`) {
|
if (msg.conversationID === `GROUP${this.imGroupId}`) {
|
||||||
const formatted = formatIMMessage(msg, this.imUserId)
|
const formatted = formatIMMessage(msg, this.imUserId, this.avatarMap)
|
||||||
// 收到改价响应消息,更新对应改价卡片状态
|
// 收到改价响应消息,更新对应改价卡片状态
|
||||||
if (formatted.type === 'price-change-response') {
|
if (formatted.type === 'price-change-response') {
|
||||||
const card = this.chatMessages.find(m => m.type === 'price-change' && m.priceChangeId === formatted.priceChangeId)
|
const card = this.chatMessages.find(m => m.type === 'price-change' && m.priceChangeId === formatted.priceChangeId)
|
||||||
|
|
@ -363,7 +374,7 @@
|
||||||
this.loadingHistory = true
|
this.loadingHistory = true
|
||||||
try {
|
try {
|
||||||
const res = await getMessageList(this.imGroupId, this.nextReqMessageID)
|
const res = await getMessageList(this.imGroupId, this.nextReqMessageID)
|
||||||
const allFormatted = res.messageList.map(m => formatIMMessage(m, this.imUserId))
|
const allFormatted = res.messageList.map(m => formatIMMessage(m, this.imUserId, this.avatarMap))
|
||||||
// 处理历史消息中的改价响应
|
// 处理历史消息中的改价响应
|
||||||
const formatted = []
|
const formatted = []
|
||||||
for (const m of allFormatted) {
|
for (const m of allFormatted) {
|
||||||
|
|
@ -402,7 +413,7 @@
|
||||||
this.inputText = ''
|
this.inputText = ''
|
||||||
try {
|
try {
|
||||||
const msg = await sendTextMessage(this.imGroupId, text)
|
const msg = await sendTextMessage(this.imGroupId, text)
|
||||||
this.chatMessages.push(formatIMMessage(msg, this.imUserId))
|
this.chatMessages.push(formatIMMessage(msg, this.imUserId, this.avatarMap))
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -435,7 +446,7 @@
|
||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
try {
|
try {
|
||||||
const msg = await sendImageMessage(this.imGroupId, res)
|
const msg = await sendImageMessage(this.imGroupId, res)
|
||||||
this.chatMessages.push(formatIMMessage(msg, this.imUserId))
|
this.chatMessages.push(formatIMMessage(msg, this.imUserId, this.avatarMap))
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -567,7 +578,7 @@
|
||||||
// 发一条文本消息记录操作(群内可见,持久化)
|
// 发一条文本消息记录操作(群内可见,持久化)
|
||||||
try {
|
try {
|
||||||
const imMsg = await sendTextMessage(this.imGroupId, `[系统提示] 单主已${actionLabel}${changeTypeLabel}改价`)
|
const imMsg = await sendTextMessage(this.imGroupId, `[系统提示] 单主已${actionLabel}${changeTypeLabel}改价`)
|
||||||
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId))
|
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId, this.avatarMap))
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
} else {
|
} else {
|
||||||
this.chatMessages.push({
|
this.chatMessages.push({
|
||||||
|
|
@ -586,7 +597,7 @@
|
||||||
if (this.imReady && this.imGroupId) {
|
if (this.imReady && this.imGroupId) {
|
||||||
try {
|
try {
|
||||||
const imMsg = await sendTextMessage(this.imGroupId, '[系统提示] 单主已完成补缴支付')
|
const imMsg = await sendTextMessage(this.imGroupId, '[系统提示] 单主已完成补缴支付')
|
||||||
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId))
|
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId, this.avatarMap))
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -733,7 +744,7 @@
|
||||||
if (this.imReady && this.imGroupId) {
|
if (this.imReady && this.imGroupId) {
|
||||||
try {
|
try {
|
||||||
const imMsg = await sendTextMessage(this.imGroupId, '[系统提示] 单主已完成补缴支付')
|
const imMsg = await sendTextMessage(this.imGroupId, '[系统提示] 单主已完成补缴支付')
|
||||||
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId))
|
this.chatMessages.push(formatIMMessage(imMsg, this.imUserId, this.avatarMap))
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,16 @@ export default {
|
||||||
this.loadUnreadCount()
|
this.loadUnreadCount()
|
||||||
this.loadChatList()
|
this.loadChatList()
|
||||||
this.updateTabBarBadge()
|
this.updateTabBarBadge()
|
||||||
|
// 定时刷新未读数(每5秒)
|
||||||
|
this._refreshTimer = setInterval(() => {
|
||||||
|
this.loadChatList()
|
||||||
|
}, 5000)
|
||||||
|
},
|
||||||
|
onHide() {
|
||||||
|
if (this._refreshTimer) {
|
||||||
|
clearInterval(this._refreshTimer)
|
||||||
|
this._refreshTimer = null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** 加载未读消息数 */
|
/** 加载未读消息数 */
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ export default {
|
||||||
/** 格式化时间(精确到年月日时分,) */
|
/** 格式化时间(精确到年月日时分,) */
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return ''
|
if (!dateStr) return ''
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ export default {
|
||||||
/** 格式化时间 */
|
/** 格式化时间 */
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return ''
|
if (!dateStr) return ''
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ export default {
|
||||||
|
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ export default {
|
||||||
|
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
},
|
},
|
||||||
|
|
@ -244,8 +244,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
await applyWithdraw({
|
await applyWithdraw({
|
||||||
amount,
|
amount,
|
||||||
paymentMethod: 'WeChat',
|
paymentMethod: 'WeChat'
|
||||||
qrCodeImage: ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
uni.showToast({ title: '提现申请已提交', icon: 'success' })
|
uni.showToast({ title: '提现申请已提交', icon: 'success' })
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@
|
||||||
<view class="profile-section">
|
<view class="profile-section">
|
||||||
<view class="profile-item">
|
<view class="profile-item">
|
||||||
<text class="item-label">头像</text>
|
<text class="item-label">头像</text>
|
||||||
<view class="item-right">
|
<view class="item-right" @click="onPickAvatar">
|
||||||
<button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
|
<image class="avatar-preview" :src="form.avatarUrl || '/static/logo.png'" mode="aspectFill"></image>
|
||||||
<image class="avatar-preview" :src="form.avatarUrl || '/static/logo.png'" mode="aspectFill"></image>
|
<image class="arrow-icon" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
||||||
<image class="arrow-icon" src="/static/ic_arrow.png" mode="aspectFit"></image>
|
|
||||||
</button>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="profile-item">
|
<view class="profile-item">
|
||||||
|
|
@ -64,20 +62,28 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
goBack() { uni.navigateBack() },
|
goBack() { uni.navigateBack() },
|
||||||
|
|
||||||
/** 微信选择头像回调 */
|
/** 选择头像 */
|
||||||
async onChooseAvatar(e) {
|
async onPickAvatar() {
|
||||||
const tempUrl = e.detail.avatarUrl
|
const self = this
|
||||||
if (!tempUrl) return
|
uni.chooseImage({
|
||||||
try {
|
count: 1,
|
||||||
uni.showLoading({ title: '上传中...' })
|
sizeType: ['compressed'],
|
||||||
const uploadRes = await uploadFile(tempUrl)
|
sourceType: ['album', 'camera'],
|
||||||
this.form.avatarUrl = uploadRes.url
|
success: async (res) => {
|
||||||
uni.hideLoading()
|
const tempUrl = res.tempFilePaths[0]
|
||||||
} catch (err) {
|
if (!tempUrl) return
|
||||||
uni.hideLoading()
|
try {
|
||||||
// 上传失败,先用临时路径显示
|
uni.showLoading({ title: '上传中...' })
|
||||||
this.form.avatarUrl = tempUrl
|
const uploadRes = await uploadFile(tempUrl)
|
||||||
}
|
self.form.avatarUrl = uploadRes.url
|
||||||
|
uni.hideLoading()
|
||||||
|
} catch (err) {
|
||||||
|
uni.hideLoading()
|
||||||
|
// 上传失败,先用临时路径显示
|
||||||
|
self.form.avatarUrl = tempUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 昵称输入框失焦(微信昵称选择后触发) */
|
/** 昵称输入框失焦(微信昵称选择后触发) */
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,18 @@ export default {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
try {
|
try {
|
||||||
await confirmOrder(this.orderId)
|
await confirmOrder(this.orderId)
|
||||||
|
|
||||||
|
// 通过 IM 通知跑腿
|
||||||
|
try {
|
||||||
|
await initIM()
|
||||||
|
const groupId = this.orderInfo.imGroupId || `order_${this.orderId}`
|
||||||
|
await sendCustomMessage(groupId, {
|
||||||
|
bizType: 'order-status',
|
||||||
|
action: 'WaitConfirm→Completed',
|
||||||
|
description: '单主已确认完成,订单已结束'
|
||||||
|
})
|
||||||
|
} catch (ex) {}
|
||||||
|
|
||||||
uni.showToast({ title: '订单已完成', icon: 'success' })
|
uni.showToast({ title: '订单已完成', icon: 'success' })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
|
@ -219,6 +231,18 @@ export default {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
try {
|
try {
|
||||||
await rejectOrder(this.orderId)
|
await rejectOrder(this.orderId)
|
||||||
|
|
||||||
|
// 通过 IM 通知跑腿
|
||||||
|
try {
|
||||||
|
await initIM()
|
||||||
|
const groupId = this.orderInfo.imGroupId || `order_${this.orderId}`
|
||||||
|
await sendCustomMessage(groupId, {
|
||||||
|
bizType: 'order-status',
|
||||||
|
action: 'WaitConfirm→InProgress',
|
||||||
|
description: '单主拒绝确认完成,订单继续进行'
|
||||||
|
})
|
||||||
|
} catch (ex) {}
|
||||||
|
|
||||||
uni.showToast({ title: '已拒绝,订单继续进行', icon: 'none' })
|
uni.showToast({ title: '已拒绝,订单继续进行', icon: 'none' })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
|
@ -233,7 +257,7 @@ export default {
|
||||||
/** 格式化时间(精确至年月日时分) */
|
/** 格式化时间(精确至年月日时分) */
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const y = d.getFullYear()
|
const y = d.getFullYear()
|
||||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
const day = String(d.getDate()).padStart(2, '0')
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ export default {
|
||||||
|
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ export default {
|
||||||
|
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ export default {
|
||||||
|
|
||||||
formatTime(dateStr) {
|
formatTime(dateStr) {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const d = new Date(dateStr)
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
||||||
const pad = (n) => String(n).padStart(2, '0')
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,19 @@ export async function initIM() {
|
||||||
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
|
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
|
||||||
console.log('[IM] SDK 就绪')
|
console.log('[IM] SDK 就绪')
|
||||||
isReady = true
|
isReady = true
|
||||||
|
// SDK就绪后同步用户头像和昵称到腾讯 IM
|
||||||
|
try {
|
||||||
|
const userInfo = uni.getStorageSync('userInfo')
|
||||||
|
if (userInfo) {
|
||||||
|
const parsed = typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo
|
||||||
|
chat.updateMyProfile({
|
||||||
|
nick: parsed.nickname || '',
|
||||||
|
avatar: parsed.avatarUrl || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[IM] 同步资料失败:', e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
|
chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
|
||||||
|
|
@ -169,9 +182,9 @@ export async function sendCustomMessage(groupId, customData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将 IM 消息转换为聊天页展示格式 */
|
/** 将 IM 消息转换为聊天页展示格式 */
|
||||||
export function formatIMMessage(msg, currentImUserId) {
|
export function formatIMMessage(msg, currentImUserId, avatarMap = {}) {
|
||||||
const isSelf = msg.from === currentImUserId
|
const isSelf = msg.from === currentImUserId
|
||||||
const avatar = msg.avatar || '/static/logo.png'
|
const avatar = avatarMap[msg.from] || msg.avatar || '/static/logo.png'
|
||||||
|
|
||||||
// 文本消息
|
// 文本消息
|
||||||
if (msg.type === TencentCloudChat.TYPES.MSG_TEXT) {
|
if (msg.type === TencentCloudChat.TYPES.MSG_TEXT) {
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,7 @@ public static class AdminEndpoints
|
||||||
o.ItemName,
|
o.ItemName,
|
||||||
Status = o.Status.ToString(),
|
Status = o.Status.ToString(),
|
||||||
o.Commission,
|
o.Commission,
|
||||||
|
o.ImGroupId,
|
||||||
OwnerId = o.OwnerId,
|
OwnerId = o.OwnerId,
|
||||||
OwnerUid = o.Owner!.Uid,
|
OwnerUid = o.Owner!.Uid,
|
||||||
OwnerNickname = o.Owner!.Nickname,
|
OwnerNickname = o.Owner!.Nickname,
|
||||||
|
|
@ -621,6 +622,7 @@ public static class AdminEndpoints
|
||||||
o.ItemName,
|
o.ItemName,
|
||||||
o.Status,
|
o.Status,
|
||||||
o.Commission,
|
o.Commission,
|
||||||
|
o.ImGroupId,
|
||||||
o.OwnerId,
|
o.OwnerId,
|
||||||
OwnerUid = string.IsNullOrWhiteSpace(o.OwnerUid) ? o.OwnerId.ToString() : o.OwnerUid,
|
OwnerUid = string.IsNullOrWhiteSpace(o.OwnerUid) ? o.OwnerId.ToString() : o.OwnerUid,
|
||||||
OwnerNickname = string.IsNullOrWhiteSpace(o.OwnerNickname) ? $"用户{o.OwnerId}" : o.OwnerNickname,
|
OwnerNickname = string.IsNullOrWhiteSpace(o.OwnerNickname) ? $"用户{o.OwnerId}" : o.OwnerNickname,
|
||||||
|
|
@ -661,5 +663,29 @@ public static class AdminEndpoints
|
||||||
return Results.BadRequest(new { code = 400, message = $"拉取聊天记录失败: {ex.Message}" });
|
return Results.BadRequest(new { code = 400, message = $"拉取聊天记录失败: {ex.Message}" });
|
||||||
}
|
}
|
||||||
}).RequireAuthorization("AdminOnly");
|
}).RequireAuthorization("AdminOnly");
|
||||||
|
|
||||||
|
// 管理端删除聊天记录(解散IM群并清除订单群ID)
|
||||||
|
app.MapDelete("/api/admin/chat-list/{groupId}", async (string groupId, AppDbContext db, TencentIMService imService) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 解散 IM 群
|
||||||
|
await imService.DestroyGroupAsync(groupId);
|
||||||
|
|
||||||
|
// 清除订单的群ID
|
||||||
|
var order = await db.Orders.FirstOrDefaultAsync(o => o.ImGroupId == groupId);
|
||||||
|
if (order != null)
|
||||||
|
{
|
||||||
|
order.ImGroupId = null;
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.Ok(new { message = "已删除" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new { code = 400, message = $"删除失败: {ex.Message}" });
|
||||||
|
}
|
||||||
|
}).RequireAuthorization("AdminOnly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,6 @@ public static class EarningEndpoints
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Amount = request.Amount,
|
Amount = request.Amount,
|
||||||
PaymentMethod = paymentMethod,
|
PaymentMethod = paymentMethod,
|
||||||
QrCodeImage = request.QrCodeImage,
|
|
||||||
Status = WithdrawalStatus.Pending,
|
Status = WithdrawalStatus.Pending,
|
||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
@ -205,9 +204,9 @@ public static class EarningEndpoints
|
||||||
w.Id,
|
w.Id,
|
||||||
w.UserId,
|
w.UserId,
|
||||||
UserNickname = w.User!.Nickname ?? ("用户" + w.UserId),
|
UserNickname = w.User!.Nickname ?? ("用户" + w.UserId),
|
||||||
|
UserUid = w.User!.Uid ?? w.UserId.ToString(),
|
||||||
w.Amount,
|
w.Amount,
|
||||||
PaymentMethod = w.PaymentMethod.ToString(),
|
PaymentMethod = w.PaymentMethod.ToString(),
|
||||||
w.QrCodeImage,
|
|
||||||
Status = w.Status.ToString(),
|
Status = w.Status.ToString(),
|
||||||
w.CreatedAt,
|
w.CreatedAt,
|
||||||
w.ProcessedAt
|
w.ProcessedAt
|
||||||
|
|
@ -229,20 +228,20 @@ public static class EarningEndpoints
|
||||||
|
|
||||||
if (request.Action == "approve")
|
if (request.Action == "approve")
|
||||||
{
|
{
|
||||||
// 先调用微信商家转账到零钱
|
// 调用微信商家转账到零钱
|
||||||
var user = await db.Users.FindAsync(withdrawal.UserId);
|
var user = await db.Users.FindAsync(withdrawal.UserId);
|
||||||
if (user == null || string.IsNullOrEmpty(user.OpenId))
|
if (user == null || string.IsNullOrEmpty(user.OpenId))
|
||||||
return Results.BadRequest(new { code = 400, message = "用户信息异常,无法转账" });
|
return Results.BadRequest(new { code = 400, message = "用户信息异常,无法转账" });
|
||||||
|
|
||||||
var amountFen = (int)(withdrawal.Amount * 100);
|
var amountFen = (int)(withdrawal.Amount * 100);
|
||||||
var batchNo = $"W{withdrawal.Id}_{DateTime.UtcNow:yyyyMMddHHmmss}";
|
var batchNo = $"W{withdrawal.Id}T{DateTime.UtcNow:yyyyMMddHHmmss}";
|
||||||
var detailNo = $"D{withdrawal.Id}_{DateTime.UtcNow:yyyyMMddHHmmss}";
|
var detailNo = $"D{withdrawal.Id}T{DateTime.UtcNow:yyyyMMddHHmmss}";
|
||||||
var (transferSuccess, transferError) = await wxPay.TransferToWallet(batchNo, detailNo, user.OpenId, amountFen, "跑腿提现到账");
|
var (transferSuccess, transferError) = await wxPay.TransferToWallet(batchNo, detailNo, user.OpenId, amountFen, "跑腿提现到账");
|
||||||
|
|
||||||
if (!transferSuccess)
|
if (!transferSuccess)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[提现] 转账失败: {transferError}");
|
Console.WriteLine($"[提现] 转账失败: {transferError}");
|
||||||
return Results.BadRequest(new { code = 400, message = $"转账失败,请稍后重试", detail = transferError });
|
return Results.BadRequest(new { code = 400, message = "转账失败,请稍后重试", detail = transferError });
|
||||||
}
|
}
|
||||||
|
|
||||||
withdrawal.Status = WithdrawalStatus.Completed;
|
withdrawal.Status = WithdrawalStatus.Completed;
|
||||||
|
|
|
||||||
|
|
@ -145,10 +145,13 @@ public static class MessageEndpoints
|
||||||
Id = o.Id,
|
Id = o.Id,
|
||||||
OrderNo = o.OrderNo,
|
OrderNo = o.OrderNo,
|
||||||
OrderType = o.OrderType.ToString(),
|
OrderType = o.OrderType.ToString(),
|
||||||
Title = o.Status == OrderStatus.InProgress ? "订单已被接取"
|
Title = o.Status == OrderStatus.InProgress
|
||||||
: o.Status == OrderStatus.Completed ? "订单已完成"
|
? (o.OwnerId == userId ? "订单已被接取" : "您已接取订单")
|
||||||
|
: o.Status == OrderStatus.Completed
|
||||||
|
? (o.RunnerId == userId ? "单主已确认完成" : "订单已完成")
|
||||||
: o.Status == OrderStatus.Cancelled ? "订单已取消"
|
: o.Status == OrderStatus.Cancelled ? "订单已取消"
|
||||||
: o.Status == OrderStatus.WaitConfirm ? "订单待确认"
|
: o.Status == OrderStatus.WaitConfirm
|
||||||
|
? (o.OwnerId == userId ? "跑腿已提交完成,请确认" : "等待单主确认完成")
|
||||||
: o.Status == OrderStatus.Appealing ? "订单申诉中"
|
: o.Status == OrderStatus.Appealing ? "订单申诉中"
|
||||||
: "订单状态变更",
|
: "订单状态变更",
|
||||||
ItemName = o.ItemName,
|
ItemName = o.ItemName,
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,8 @@ public static class OrderEndpoints
|
||||||
AcceptedAt = visibleAcceptedAt,
|
AcceptedAt = visibleAcceptedAt,
|
||||||
CompletedAt = visibleCompletedAt,
|
CompletedAt = visibleCompletedAt,
|
||||||
RunnerNickname = runnerNickname,
|
RunnerNickname = runnerNickname,
|
||||||
|
RunnerAvatar = order.Runner?.AvatarUrl,
|
||||||
|
OwnerAvatar = order.Owner?.AvatarUrl,
|
||||||
RunnerUid = runnerUid,
|
RunnerUid = runnerUid,
|
||||||
RunnerPhone = runnerPhone
|
RunnerPhone = runnerPhone
|
||||||
};
|
};
|
||||||
|
|
@ -513,6 +515,7 @@ public static class OrderEndpoints
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
Console.WriteLine($"[接单] 订单 {order.Id} 接单成功,准备创建群组");
|
||||||
}
|
}
|
||||||
catch (DbUpdateConcurrencyException)
|
catch (DbUpdateConcurrencyException)
|
||||||
{
|
{
|
||||||
|
|
@ -525,16 +528,19 @@ public static class OrderEndpoints
|
||||||
var groupId = $"order_{order.Id}";
|
var groupId = $"order_{order.Id}";
|
||||||
var ownerImId = $"user_{order.OwnerId}";
|
var ownerImId = $"user_{order.OwnerId}";
|
||||||
var runnerImId = $"user_{userId}";
|
var runnerImId = $"user_{userId}";
|
||||||
|
Console.WriteLine($"[IM] 准备创建群组: {groupId}, 单主: {ownerImId}, 跑腿: {runnerImId}");
|
||||||
var result = await imService.CreateGroupAsync(groupId, $"订单{order.OrderNo}", ownerImId, runnerImId);
|
var result = await imService.CreateGroupAsync(groupId, $"订单{order.OrderNo}", ownerImId, runnerImId);
|
||||||
|
Console.WriteLine($"[IM] 创建群组结果: {result}");
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
order.ImGroupId = result;
|
order.ImGroupId = result;
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
Console.WriteLine($"[IM] 群组ID已保存: {result}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[IM] 创建群组失败: {ex.Message}");
|
Console.WriteLine($"[IM] 创建群组失败: {ex.Message}\n{ex.StackTrace}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Results.Ok(new AcceptOrderResponse
|
return Results.Ok(new AcceptOrderResponse
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,6 @@ public class WithdrawRequest
|
||||||
/// <summary>收款方式:WeChat 或 Alipay</summary>
|
/// <summary>收款方式:WeChat 或 Alipay</summary>
|
||||||
[Required(ErrorMessage = "收款方式不能为空")]
|
[Required(ErrorMessage = "收款方式不能为空")]
|
||||||
public string PaymentMethod { get; set; } = string.Empty;
|
public string PaymentMethod { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>收款二维码图片</summary>
|
|
||||||
[Required(ErrorMessage = "收款二维码不能为空")]
|
|
||||||
public string QrCodeImage { get; set; } = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ public class OrderResponse
|
||||||
public DateTime? CompletedAt { get; set; }
|
public DateTime? CompletedAt { get; set; }
|
||||||
/// <summary>跑腿昵称</summary>
|
/// <summary>跑腿昵称</summary>
|
||||||
public string? RunnerNickname { get; set; }
|
public string? RunnerNickname { get; set; }
|
||||||
|
/// <summary>跑腿头像</summary>
|
||||||
|
public string? RunnerAvatar { get; set; }
|
||||||
|
/// <summary>单主头像</summary>
|
||||||
|
public string? OwnerAvatar { get; set; }
|
||||||
/// <summary>跑腿 UID</summary>
|
/// <summary>跑腿 UID</summary>
|
||||||
public int? RunnerUid { get; set; }
|
public int? RunnerUid { get; set; }
|
||||||
/// <summary>跑腿手机号(认证时填写的手机号,仅单主可见)</summary>
|
/// <summary>跑腿手机号(认证时填写的手机号,仅单主可见)</summary>
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ public class TencentIMService
|
||||||
|
|
||||||
var body = new
|
var body = new
|
||||||
{
|
{
|
||||||
Type = "Private",
|
Type = "Work",
|
||||||
GroupId = groupId,
|
GroupId = groupId,
|
||||||
Name = groupName,
|
Name = groupName,
|
||||||
Owner_Account = ownerImId,
|
Owner_Account = ownerImId,
|
||||||
|
|
@ -131,16 +131,31 @@ public class TencentIMService
|
||||||
var random = Random.Shared.Next(100000, 999999);
|
var random = Random.Shared.Next(100000, 999999);
|
||||||
var url = $"https://console.tim.qq.com/v4/group_open_http_svc/group_msg_get_simple?sdkappid={_sdkAppId}&identifier={AdminAccount}&usersig={adminSig}&random={random}&contenttype=json";
|
var url = $"https://console.tim.qq.com/v4/group_open_http_svc/group_msg_get_simple?sdkappid={_sdkAppId}&identifier={AdminAccount}&usersig={adminSig}&random={random}&contenttype=json";
|
||||||
|
|
||||||
var body = new
|
// ReqMsgSeq 为 0 时不传该字段,让腾讯 IM 自动返回最新消息
|
||||||
{
|
object body = reqMsgSeq > 0
|
||||||
GroupId = groupId,
|
? new { GroupId = groupId, ReqMsgSeq = reqMsgSeq, ReqMsgNumber = reqMsgNumber }
|
||||||
ReqMsgSeq = reqMsgSeq,
|
: new { GroupId = groupId, ReqMsgNumber = reqMsgNumber };
|
||||||
ReqMsgNumber = reqMsgNumber
|
|
||||||
};
|
|
||||||
|
|
||||||
var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
|
var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
|
||||||
var response = await _httpClient.PostAsync(url, content);
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
Console.WriteLine($"[IM] 拉取群消息 {groupId}: {json.Substring(0, Math.Min(json.Length, 500))}");
|
||||||
return JsonSerializer.Deserialize<JsonElement>(json);
|
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解散 IM 群组
|
||||||
|
/// </summary>
|
||||||
|
public async Task DestroyGroupAsync(string groupId)
|
||||||
|
{
|
||||||
|
var adminSig = GenerateUserSig(AdminAccount);
|
||||||
|
var random = Random.Shared.Next(100000, 999999);
|
||||||
|
var url = $"https://console.tim.qq.com/v4/group_open_http_svc/destroy_group?sdkappid={_sdkAppId}&identifier={AdminAccount}&usersig={adminSig}&random={random}&contenttype=json";
|
||||||
|
|
||||||
|
var body = new { GroupId = groupId };
|
||||||
|
var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
|
||||||
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
Console.WriteLine($"[IM] 解散群组 {groupId}: {json}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user