From 681d2b5fe818b82aa3256fcb569d77be2802fd41 Mon Sep 17 00:00:00 2001
From: 18631081161 <2088094923@qq.com>
Date: Thu, 2 Apr 2026 16:55:18 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.vscode/settings.json | 3 +-
admin/src/views/ChatRecords.vue | 26 +++++++----
admin/src/views/Withdrawals.vue | 21 +++------
miniapp/App.vue | 62 ++++++++++++++++----------
miniapp/pages/message/chat.vue | 25 ++++++++---
miniapp/pages/message/message.vue | 10 +++++
miniapp/pages/message/order-notify.vue | 2 +-
miniapp/pages/message/system-msg.vue | 2 +-
miniapp/pages/mine/earnings-record.vue | 2 +-
miniapp/pages/mine/earnings.vue | 5 +--
miniapp/pages/mine/profile.vue | 44 ++++++++++--------
miniapp/pages/order/complete-order.vue | 26 ++++++++++-
miniapp/pages/order/my-orders.vue | 2 +-
miniapp/pages/order/my-taken.vue | 2 +-
miniapp/pages/order/order-detail.vue | 2 +-
miniapp/utils/im.js | 17 ++++++-
server/Endpoints/AdminEndpoints.cs | 26 +++++++++++
server/Endpoints/EarningEndpoints.cs | 11 +++--
server/Endpoints/MessageEndpoints.cs | 9 ++--
server/Endpoints/OrderEndpoints.cs | 8 +++-
server/Models/Dtos/EarningDtos.cs | 4 --
server/Models/Dtos/OrderDtos.cs | 4 ++
server/Services/TencentIMService.cs | 29 +++++++++---
23 files changed, 235 insertions(+), 107 deletions(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3b66410..bf5774d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,4 @@
{
- "git.ignoreLimitWarning": true
+ "git.ignoreLimitWarning": true,
+ "kiro.chat.autoSummarize.threshold": 30
}
\ No newline at end of file
diff --git a/admin/src/views/ChatRecords.vue b/admin/src/views/ChatRecords.vue
index 0a54546..bd26297 100644
--- a/admin/src/views/ChatRecords.vue
+++ b/admin/src/views/ChatRecords.vue
@@ -93,6 +93,7 @@
diff --git a/miniapp/pages/message/chat.vue b/miniapp/pages/message/chat.vue
index 338ce23..95d3f6f 100644
--- a/miniapp/pages/message/chat.vue
+++ b/miniapp/pages/message/chat.vue
@@ -254,6 +254,17 @@
const userStore = useUserStore()
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() {
const type = this.orderInfo.orderType
return type === 'Purchase' || type === 'Food'
@@ -329,7 +340,7 @@
for (const msg of msgList) {
// 只处理当前群的消息
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') {
const card = this.chatMessages.find(m => m.type === 'price-change' && m.priceChangeId === formatted.priceChangeId)
@@ -363,7 +374,7 @@
this.loadingHistory = true
try {
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 = []
for (const m of allFormatted) {
@@ -402,7 +413,7 @@
this.inputText = ''
try {
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()
} catch (e) {
uni.showToast({
@@ -435,7 +446,7 @@
success: async (res) => {
try {
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()
} catch (e) {
uni.showToast({
@@ -567,7 +578,7 @@
// 发一条文本消息记录操作(群内可见,持久化)
try {
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) {}
} else {
this.chatMessages.push({
@@ -586,7 +597,7 @@
if (this.imReady && this.imGroupId) {
try {
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 (e) {
@@ -733,7 +744,7 @@
if (this.imReady && this.imGroupId) {
try {
const imMsg = await sendTextMessage(this.imGroupId, '[系统提示] 单主已完成补缴支付')
- this.chatMessages.push(formatIMMessage(imMsg, this.imUserId))
+ this.chatMessages.push(formatIMMessage(imMsg, this.imUserId, this.avatarMap))
this.scrollToBottom()
} catch (ex) {}
}
diff --git a/miniapp/pages/message/message.vue b/miniapp/pages/message/message.vue
index 0020786..e8fa0b9 100644
--- a/miniapp/pages/message/message.vue
+++ b/miniapp/pages/message/message.vue
@@ -113,6 +113,16 @@ export default {
this.loadUnreadCount()
this.loadChatList()
this.updateTabBarBadge()
+ // 定时刷新未读数(每5秒)
+ this._refreshTimer = setInterval(() => {
+ this.loadChatList()
+ }, 5000)
+ },
+ onHide() {
+ if (this._refreshTimer) {
+ clearInterval(this._refreshTimer)
+ this._refreshTimer = null
+ }
},
methods: {
/** 加载未读消息数 */
diff --git a/miniapp/pages/message/order-notify.vue b/miniapp/pages/message/order-notify.vue
index 77b55de..47db72a 100644
--- a/miniapp/pages/message/order-notify.vue
+++ b/miniapp/pages/message/order-notify.vue
@@ -137,7 +137,7 @@ export default {
/** 格式化时间(精确到年月日时分,) */
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
diff --git a/miniapp/pages/message/system-msg.vue b/miniapp/pages/message/system-msg.vue
index 78d9be7..3898224 100644
--- a/miniapp/pages/message/system-msg.vue
+++ b/miniapp/pages/message/system-msg.vue
@@ -116,7 +116,7 @@ export default {
/** 格式化时间 */
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
diff --git a/miniapp/pages/mine/earnings-record.vue b/miniapp/pages/mine/earnings-record.vue
index 1c952cd..791b8a6 100644
--- a/miniapp/pages/mine/earnings-record.vue
+++ b/miniapp/pages/mine/earnings-record.vue
@@ -101,7 +101,7 @@ export default {
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}
diff --git a/miniapp/pages/mine/earnings.vue b/miniapp/pages/mine/earnings.vue
index e0478b1..e72ec38 100644
--- a/miniapp/pages/mine/earnings.vue
+++ b/miniapp/pages/mine/earnings.vue
@@ -180,7 +180,7 @@ export default {
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
@@ -244,8 +244,7 @@ export default {
try {
await applyWithdraw({
amount,
- paymentMethod: 'WeChat',
- qrCodeImage: ''
+ paymentMethod: 'WeChat'
})
uni.showToast({ title: '提现申请已提交', icon: 'success' })
diff --git a/miniapp/pages/mine/profile.vue b/miniapp/pages/mine/profile.vue
index 55ad7bf..356467b 100644
--- a/miniapp/pages/mine/profile.vue
+++ b/miniapp/pages/mine/profile.vue
@@ -16,11 +16,9 @@
头像
-
-
+
+
+
@@ -64,20 +62,28 @@ export default {
methods: {
goBack() { uni.navigateBack() },
- /** 微信选择头像回调 */
- async onChooseAvatar(e) {
- const tempUrl = e.detail.avatarUrl
- if (!tempUrl) return
- try {
- uni.showLoading({ title: '上传中...' })
- const uploadRes = await uploadFile(tempUrl)
- this.form.avatarUrl = uploadRes.url
- uni.hideLoading()
- } catch (err) {
- uni.hideLoading()
- // 上传失败,先用临时路径显示
- this.form.avatarUrl = tempUrl
- }
+ /** 选择头像 */
+ async onPickAvatar() {
+ const self = this
+ uni.chooseImage({
+ count: 1,
+ sizeType: ['compressed'],
+ sourceType: ['album', 'camera'],
+ success: async (res) => {
+ const tempUrl = res.tempFilePaths[0]
+ if (!tempUrl) return
+ try {
+ uni.showLoading({ title: '上传中...' })
+ const uploadRes = await uploadFile(tempUrl)
+ self.form.avatarUrl = uploadRes.url
+ uni.hideLoading()
+ } catch (err) {
+ uni.hideLoading()
+ // 上传失败,先用临时路径显示
+ self.form.avatarUrl = tempUrl
+ }
+ }
+ })
},
/** 昵称输入框失焦(微信昵称选择后触发) */
diff --git a/miniapp/pages/order/complete-order.vue b/miniapp/pages/order/complete-order.vue
index 4db5a6e..2018c96 100644
--- a/miniapp/pages/order/complete-order.vue
+++ b/miniapp/pages/order/complete-order.vue
@@ -199,6 +199,18 @@ export default {
if (!res.confirm) return
try {
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' })
setTimeout(() => {
uni.navigateBack()
@@ -219,6 +231,18 @@ export default {
if (!res.confirm) return
try {
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' })
setTimeout(() => {
uni.navigateBack()
@@ -233,7 +257,7 @@ export default {
/** 格式化时间(精确至年月日时分) */
formatTime(dateStr) {
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 m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
diff --git a/miniapp/pages/order/my-orders.vue b/miniapp/pages/order/my-orders.vue
index c74f5c7..10b2ede 100644
--- a/miniapp/pages/order/my-orders.vue
+++ b/miniapp/pages/order/my-orders.vue
@@ -275,7 +275,7 @@ export default {
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
diff --git a/miniapp/pages/order/my-taken.vue b/miniapp/pages/order/my-taken.vue
index 21d868b..3086a5d 100644
--- a/miniapp/pages/order/my-taken.vue
+++ b/miniapp/pages/order/my-taken.vue
@@ -200,7 +200,7 @@ export default {
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
diff --git a/miniapp/pages/order/order-detail.vue b/miniapp/pages/order/order-detail.vue
index a735768..c6ea7e1 100644
--- a/miniapp/pages/order/order-detail.vue
+++ b/miniapp/pages/order/order-detail.vue
@@ -323,7 +323,7 @@ export default {
formatTime(dateStr) {
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')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
diff --git a/miniapp/utils/im.js b/miniapp/utils/im.js
index d53317e..b61cf17 100644
--- a/miniapp/utils/im.js
+++ b/miniapp/utils/im.js
@@ -28,6 +28,19 @@ export async function initIM() {
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
console.log('[IM] SDK 就绪')
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) => {
@@ -169,9 +182,9 @@ export async function sendCustomMessage(groupId, customData) {
}
/** 将 IM 消息转换为聊天页展示格式 */
-export function formatIMMessage(msg, currentImUserId) {
+export function formatIMMessage(msg, currentImUserId, avatarMap = {}) {
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) {
diff --git a/server/Endpoints/AdminEndpoints.cs b/server/Endpoints/AdminEndpoints.cs
index ea05978..aad9f50 100644
--- a/server/Endpoints/AdminEndpoints.cs
+++ b/server/Endpoints/AdminEndpoints.cs
@@ -601,6 +601,7 @@ public static class AdminEndpoints
o.ItemName,
Status = o.Status.ToString(),
o.Commission,
+ o.ImGroupId,
OwnerId = o.OwnerId,
OwnerUid = o.Owner!.Uid,
OwnerNickname = o.Owner!.Nickname,
@@ -621,6 +622,7 @@ public static class AdminEndpoints
o.ItemName,
o.Status,
o.Commission,
+ o.ImGroupId,
o.OwnerId,
OwnerUid = string.IsNullOrWhiteSpace(o.OwnerUid) ? o.OwnerId.ToString() : o.OwnerUid,
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}" });
}
}).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");
}
}
diff --git a/server/Endpoints/EarningEndpoints.cs b/server/Endpoints/EarningEndpoints.cs
index 66cf717..20c8dd1 100644
--- a/server/Endpoints/EarningEndpoints.cs
+++ b/server/Endpoints/EarningEndpoints.cs
@@ -129,7 +129,6 @@ public static class EarningEndpoints
UserId = userId,
Amount = request.Amount,
PaymentMethod = paymentMethod,
- QrCodeImage = request.QrCodeImage,
Status = WithdrawalStatus.Pending,
CreatedAt = DateTime.UtcNow
};
@@ -205,9 +204,9 @@ public static class EarningEndpoints
w.Id,
w.UserId,
UserNickname = w.User!.Nickname ?? ("用户" + w.UserId),
+ UserUid = w.User!.Uid ?? w.UserId.ToString(),
w.Amount,
PaymentMethod = w.PaymentMethod.ToString(),
- w.QrCodeImage,
Status = w.Status.ToString(),
w.CreatedAt,
w.ProcessedAt
@@ -229,20 +228,20 @@ public static class EarningEndpoints
if (request.Action == "approve")
{
- // 先调用微信商家转账到零钱
+ // 调用微信商家转账到零钱
var user = await db.Users.FindAsync(withdrawal.UserId);
if (user == null || string.IsNullOrEmpty(user.OpenId))
return Results.BadRequest(new { code = 400, message = "用户信息异常,无法转账" });
var amountFen = (int)(withdrawal.Amount * 100);
- var batchNo = $"W{withdrawal.Id}_{DateTime.UtcNow:yyyyMMddHHmmss}";
- var detailNo = $"D{withdrawal.Id}_{DateTime.UtcNow:yyyyMMddHHmmss}";
+ var batchNo = $"W{withdrawal.Id}T{DateTime.UtcNow:yyyyMMddHHmmss}";
+ var detailNo = $"D{withdrawal.Id}T{DateTime.UtcNow:yyyyMMddHHmmss}";
var (transferSuccess, transferError) = await wxPay.TransferToWallet(batchNo, detailNo, user.OpenId, amountFen, "跑腿提现到账");
if (!transferSuccess)
{
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;
diff --git a/server/Endpoints/MessageEndpoints.cs b/server/Endpoints/MessageEndpoints.cs
index 25ab44a..c8ccb94 100644
--- a/server/Endpoints/MessageEndpoints.cs
+++ b/server/Endpoints/MessageEndpoints.cs
@@ -145,10 +145,13 @@ public static class MessageEndpoints
Id = o.Id,
OrderNo = o.OrderNo,
OrderType = o.OrderType.ToString(),
- Title = o.Status == OrderStatus.InProgress ? "订单已被接取"
- : o.Status == OrderStatus.Completed ? "订单已完成"
+ Title = o.Status == OrderStatus.InProgress
+ ? (o.OwnerId == userId ? "订单已被接取" : "您已接取订单")
+ : o.Status == OrderStatus.Completed
+ ? (o.RunnerId == userId ? "单主已确认完成" : "订单已完成")
: o.Status == OrderStatus.Cancelled ? "订单已取消"
- : o.Status == OrderStatus.WaitConfirm ? "订单待确认"
+ : o.Status == OrderStatus.WaitConfirm
+ ? (o.OwnerId == userId ? "跑腿已提交完成,请确认" : "等待单主确认完成")
: o.Status == OrderStatus.Appealing ? "订单申诉中"
: "订单状态变更",
ItemName = o.ItemName,
diff --git a/server/Endpoints/OrderEndpoints.cs b/server/Endpoints/OrderEndpoints.cs
index 6bc73fb..2f87012 100644
--- a/server/Endpoints/OrderEndpoints.cs
+++ b/server/Endpoints/OrderEndpoints.cs
@@ -332,6 +332,8 @@ public static class OrderEndpoints
AcceptedAt = visibleAcceptedAt,
CompletedAt = visibleCompletedAt,
RunnerNickname = runnerNickname,
+ RunnerAvatar = order.Runner?.AvatarUrl,
+ OwnerAvatar = order.Owner?.AvatarUrl,
RunnerUid = runnerUid,
RunnerPhone = runnerPhone
};
@@ -513,6 +515,7 @@ public static class OrderEndpoints
try
{
await db.SaveChangesAsync();
+ Console.WriteLine($"[接单] 订单 {order.Id} 接单成功,准备创建群组");
}
catch (DbUpdateConcurrencyException)
{
@@ -525,16 +528,19 @@ public static class OrderEndpoints
var groupId = $"order_{order.Id}";
var ownerImId = $"user_{order.OwnerId}";
var runnerImId = $"user_{userId}";
+ Console.WriteLine($"[IM] 准备创建群组: {groupId}, 单主: {ownerImId}, 跑腿: {runnerImId}");
var result = await imService.CreateGroupAsync(groupId, $"订单{order.OrderNo}", ownerImId, runnerImId);
+ Console.WriteLine($"[IM] 创建群组结果: {result}");
if (result != null)
{
order.ImGroupId = result;
await db.SaveChangesAsync();
+ Console.WriteLine($"[IM] 群组ID已保存: {result}");
}
}
catch (Exception ex)
{
- Console.WriteLine($"[IM] 创建群组失败: {ex.Message}");
+ Console.WriteLine($"[IM] 创建群组失败: {ex.Message}\n{ex.StackTrace}");
}
return Results.Ok(new AcceptOrderResponse
diff --git a/server/Models/Dtos/EarningDtos.cs b/server/Models/Dtos/EarningDtos.cs
index f7a1ade..6510c8b 100644
--- a/server/Models/Dtos/EarningDtos.cs
+++ b/server/Models/Dtos/EarningDtos.cs
@@ -61,10 +61,6 @@ public class WithdrawRequest
/// 收款方式:WeChat 或 Alipay
[Required(ErrorMessage = "收款方式不能为空")]
public string PaymentMethod { get; set; } = string.Empty;
-
- /// 收款二维码图片
- [Required(ErrorMessage = "收款二维码不能为空")]
- public string QrCodeImage { get; set; } = string.Empty;
}
///
diff --git a/server/Models/Dtos/OrderDtos.cs b/server/Models/Dtos/OrderDtos.cs
index 80813f3..86f3c59 100644
--- a/server/Models/Dtos/OrderDtos.cs
+++ b/server/Models/Dtos/OrderDtos.cs
@@ -81,6 +81,10 @@ public class OrderResponse
public DateTime? CompletedAt { get; set; }
/// 跑腿昵称
public string? RunnerNickname { get; set; }
+ /// 跑腿头像
+ public string? RunnerAvatar { get; set; }
+ /// 单主头像
+ public string? OwnerAvatar { get; set; }
/// 跑腿 UID
public int? RunnerUid { get; set; }
/// 跑腿手机号(认证时填写的手机号,仅单主可见)
diff --git a/server/Services/TencentIMService.cs b/server/Services/TencentIMService.cs
index 15dad8c..ec15968 100644
--- a/server/Services/TencentIMService.cs
+++ b/server/Services/TencentIMService.cs
@@ -94,7 +94,7 @@ public class TencentIMService
var body = new
{
- Type = "Private",
+ Type = "Work",
GroupId = groupId,
Name = groupName,
Owner_Account = ownerImId,
@@ -131,16 +131,31 @@ public class TencentIMService
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 body = new
- {
- GroupId = groupId,
- ReqMsgSeq = reqMsgSeq,
- ReqMsgNumber = reqMsgNumber
- };
+ // ReqMsgSeq 为 0 时不传该字段,让腾讯 IM 自动返回最新消息
+ object body = reqMsgSeq > 0
+ ? new { GroupId = groupId, ReqMsgSeq = reqMsgSeq, ReqMsgNumber = reqMsgNumber }
+ : new { GroupId = groupId, ReqMsgNumber = reqMsgNumber };
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.Substring(0, Math.Min(json.Length, 500))}");
return JsonSerializer.Deserialize(json);
}
+
+ ///
+ /// 解散 IM 群组
+ ///
+ 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}");
+ }
}