582 lines
15 KiB
Plaintext
582 lines
15 KiB
Plaintext
/**
|
||
* 消息数据服务 - 真实 supadb 集成版本
|
||
* 严格遵循 UTS Android 要求,使用真实的 AkSupa 组件
|
||
*/
|
||
|
||
import supa from '@/components/supadb/aksupainstance.uts'
|
||
import type { AkReqResponse } from '@/uni_modules/ak-req/index.uts'
|
||
import type {
|
||
Message,
|
||
MessageType,
|
||
MessageRecipient,
|
||
MessageGroup,
|
||
MessageStats,
|
||
MessageListParams,
|
||
SendMessageParams,
|
||
UserOption,
|
||
MessageSenderType,
|
||
MessageReceiverType,
|
||
MessageContentType,
|
||
MessageStatus,
|
||
RecipientStatus,
|
||
DeliveryMethod,
|
||
GroupType,
|
||
MemberRole,
|
||
MemberStatus
|
||
} from './msgTypes.uts'
|
||
|
||
// 定义 GroupMember 类型,如果在 msgTypes.uts 中没有的话
|
||
type GroupMember = {
|
||
id : string;
|
||
group_id : string;
|
||
user_id : string;
|
||
role : MemberRole;
|
||
status : MemberStatus;
|
||
joined_at : string;
|
||
updated_at : string;
|
||
}
|
||
|
||
/**
|
||
* 消息数据服务- 真实 supadb 版本
|
||
* 提供消息系统的所有数据操作
|
||
* 统一使用 aksupainstance.uts
|
||
*/
|
||
export class MsgDataServiceReal {
|
||
|
||
/**
|
||
* 获取消息类型列表
|
||
*/
|
||
static async getMessageTypes() : Promise<AkReqResponse<Array<MessageType>>> {
|
||
try {
|
||
const response = await supa
|
||
.from('ak_message_types')
|
||
.select('*', {})
|
||
.eq('is_active', true)
|
||
.order('priority', { ascending: false })
|
||
.executeAs<MessageType>()
|
||
|
||
if (response.status >= 200 && response.status < 300 && response.data !== null) {
|
||
console.log(response)
|
||
return response
|
||
}
|
||
|
||
// 如果没有数据,返回默认的消息类型
|
||
return createErrorResponse<Array<MessageType>>([{
|
||
id: 'default',
|
||
code: 'default',
|
||
name: '普通消息',
|
||
description: '默认消息类型',
|
||
icon: null,
|
||
color: '#757575',
|
||
priority: 50,
|
||
is_system: false,
|
||
is_active: true,
|
||
auto_read_timeout: null,
|
||
retention_days: 30,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
}], '无消息类型数据', 200)
|
||
} catch (error) {
|
||
// 出错时也返回默认的消息类型
|
||
return createErrorResponse<Array<MessageType>>([{
|
||
id: 'default',
|
||
code: 'default',
|
||
name: '普通消息',
|
||
description: '默认消息类型',
|
||
icon: null,
|
||
color: '#757575',
|
||
priority: 50,
|
||
is_system: false,
|
||
is_active: true,
|
||
auto_read_timeout: null,
|
||
retention_days: 30,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
}], typeof error === 'string' ? error : error?.message ?? '获取消息类型失败', 200)
|
||
}
|
||
}
|
||
/**
|
||
* 获取消息列表
|
||
*/
|
||
static async getMessages(params : MessageListParams) : Promise<AkReqResponse<Array<Message>>> {
|
||
try {
|
||
let query = supa
|
||
.from('ak_messages')
|
||
.select(`
|
||
*,
|
||
message_type:ak_message_types(*),
|
||
recipients:ak_message_recipients(*)
|
||
`, {})
|
||
.eq('is_deleted', false) // 添加筛选条件
|
||
if (params.message_type != null) {
|
||
query = query.eq('message_type', params.message_type as string)
|
||
}
|
||
if (params.sender_type != null) {
|
||
query = query.eq('sender_type', params.sender_type as string)
|
||
}
|
||
if (params.sender_id != null) {
|
||
query = query.eq('sender_id', params.sender_id as string)
|
||
}
|
||
if (params.receiver_type != null) {
|
||
query = query.eq('receiver_type', params.receiver_type as string)
|
||
}
|
||
if (params.receiver_id != null) {
|
||
query = query.eq('receiver_id', params.receiver_id as string)
|
||
}
|
||
if (params.status != null) {
|
||
query = query.eq('status', params.status as string)
|
||
}
|
||
if (params.is_urgent != null) {
|
||
query = query.eq('is_urgent', params.is_urgent as boolean)
|
||
}
|
||
const keyword = params.search
|
||
if (keyword != null && keyword !== '') {
|
||
query = query.or(`title.ilike.%${keyword}%,content.ilike.%${keyword}%`)
|
||
}
|
||
const limit = params.limit ?? 20
|
||
const offset = params.offset ?? 0
|
||
// 分页和排序
|
||
const page = Math.floor(offset / limit) + 1
|
||
const response = await query
|
||
.order('created_at', { ascending: false })
|
||
.range(offset, offset + limit - 1)
|
||
.executeAs<Message>()
|
||
// .execute()
|
||
console.log(response)
|
||
return response
|
||
} catch (error) {
|
||
console.log(error)
|
||
return createErrorResponse<Array<UTSJSONObject>>([],
|
||
typeof error === 'string' ? error : error?.message ?? '获取消息列表失败')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取单条消息详情
|
||
*/
|
||
static async getMessageById(id : string) : Promise<AkReqResponse<Message>> {
|
||
try {
|
||
const response = await supa
|
||
.from('ak_messages')
|
||
.select(`
|
||
*,
|
||
message_type:ak_message_types(*),
|
||
recipients:ak_message_recipients(*)
|
||
`, {})
|
||
.eq('id', id)
|
||
.single()
|
||
.executeAs<Message>()
|
||
|
||
return response
|
||
} catch (error) {
|
||
return {
|
||
status: 500,
|
||
data: null,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '获取消息内容失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<Message>
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送消<E98081>?
|
||
*/
|
||
static async sendMessage(params : SendMessageParams) : Promise<AkReqResponse<Message>> {
|
||
try {
|
||
|
||
|
||
// 构建消息数据
|
||
const messageData = {
|
||
message_type_id: params.message_type_id,
|
||
sender_type: params.sender_type,
|
||
sender_id: params.sender_id,
|
||
sender_name: params.sender_name,
|
||
receiver_type: params.receiver_type,
|
||
receiver_id: params.receiver_id,
|
||
title: params.title,
|
||
content: params.content,
|
||
content_type: params.content_type !== null ? params.content_type : 'text',
|
||
attachments: params.attachments,
|
||
media_urls: params.media_urls,
|
||
metadata: params.metadata,
|
||
is_urgent: params.is_urgent !== null ? params.is_urgent : false,
|
||
scheduled_at: params.scheduled_at,
|
||
expires_at: params.expires_at,
|
||
status: 'pending',
|
||
is_deleted: false,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
}
|
||
|
||
// 插入消息
|
||
const response = await supa
|
||
.from('ak_messages')
|
||
.insert(messageData)
|
||
.single()
|
||
.executeAs<Message>()
|
||
|
||
if (response.status >= 200 && response.status < 300) {
|
||
const message = response.data as Message
|
||
|
||
// 创建接收记录
|
||
// 批量接收人安全判断
|
||
const receiversArr = params.receivers ?? []
|
||
if (Array.isArray(receiversArr) && receiversArr.length > 0) {
|
||
const recipients = receiversArr.map(receiverId => ({
|
||
message_id: message.id,
|
||
receiver_type: params.receiver_type,
|
||
receiver_id: receiverId,
|
||
status: 'unread',
|
||
is_deleted: false,
|
||
created_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
}))
|
||
for (const rec of recipients) {
|
||
await supa
|
||
.from('ak_message_recipients')
|
||
.insert(rec)
|
||
.executeAs<MessageRecipient>()
|
||
}
|
||
}
|
||
|
||
return response
|
||
} else {
|
||
return response
|
||
}
|
||
} catch (error) {
|
||
return {
|
||
status: 500,
|
||
data: null,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '发送消息失败'),
|
||
headers: {},
|
||
total: null,
|
||
page: null,
|
||
limit: null,
|
||
hasmore: false,
|
||
origin: null
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 标记消息为已读
|
||
*/
|
||
static async markAsRead(messageId : string, receiverId : string) : Promise<AkReqResponse<boolean>> {
|
||
try {
|
||
// 验证参数不能为空
|
||
if (messageId == null || messageId === '' || receiverId == null || receiverId === '' || receiverId.trim() === '') {
|
||
return createErrorResponse<boolean>(
|
||
false,
|
||
'参数错误:消息ID和接收者ID不能为空'
|
||
)
|
||
}
|
||
|
||
const response = await supa
|
||
.from('ak_message_recipients')
|
||
.update({
|
||
status: 'read',
|
||
read_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.eq('message_id', messageId)
|
||
.eq('recipient_id', receiverId)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
|
||
return {
|
||
status: response.status,
|
||
data: Array.isArray(response.data) ,
|
||
headers: response.headers,
|
||
error: response.error,
|
||
total: response.total,
|
||
page: response.page,
|
||
limit: response.limit,
|
||
hasmore: response.hasmore,
|
||
origin: response.origin
|
||
}
|
||
} catch (error) {
|
||
return createErrorResponse<boolean>(
|
||
false,
|
||
typeof error === 'string' ? error : error?.message ?? '标记已读失败'
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除消息
|
||
*/
|
||
static async deleteMessage(messageId : string, userId : string) : Promise<AkReqResponse<boolean>> {
|
||
try {
|
||
// 软删除消息(对发送者)
|
||
await supa
|
||
.from('ak_messages')
|
||
.update({
|
||
is_deleted: true,
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.eq('id', messageId)
|
||
.eq('sender_id', userId)
|
||
.executeAs<Array<Message>>()
|
||
|
||
// 软删除接收记录(对接收者)
|
||
await supa
|
||
.from('ak_message_recipients')
|
||
.update({
|
||
is_deleted: true,
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.eq('message_id', messageId)
|
||
.eq('recipient_id', userId)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
const aaa = {
|
||
status: 200,
|
||
data: true,
|
||
error: null,
|
||
origin: null,
|
||
headers: {}
|
||
}
|
||
return aaa
|
||
} catch (error) {
|
||
const aaa = {
|
||
status: 500,
|
||
data: false,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '删除消息失败'),
|
||
origin: null,
|
||
headers: {}
|
||
}
|
||
return aaa
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量操作消息
|
||
*/
|
||
static async batchOperation(
|
||
messageIds : Array<string>,
|
||
operation : string,
|
||
userId : string
|
||
) : Promise<AkReqResponse<boolean>> {
|
||
try {
|
||
// 验证参数不能为空
|
||
if (userId == null || userId === '' || userId.trim() === '') {
|
||
return createErrorResponse<boolean>(
|
||
false,
|
||
'参数错误:用户ID不能为空'
|
||
)
|
||
}
|
||
|
||
if (messageIds == null || messageIds.length === 0) {
|
||
return createErrorResponse<boolean>(
|
||
false,
|
||
'参数错误:消息ID列表不能为空'
|
||
)
|
||
}
|
||
|
||
switch (operation) {
|
||
case 'read':
|
||
await supa
|
||
.from('ak_message_recipients')
|
||
.update({
|
||
status: 'read',
|
||
read_at: new Date().toISOString(),
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.in('message_id', messageIds.map(x => x as any))
|
||
.eq('recipient_id', userId)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
break
|
||
|
||
case 'delete':
|
||
await supa
|
||
.from('ak_message_recipients')
|
||
.update({
|
||
is_deleted: true,
|
||
updated_at: new Date().toISOString()
|
||
})
|
||
.in('message_id', messageIds.map(x => x as any))
|
||
.eq('recipient_id', userId)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
break
|
||
|
||
default:
|
||
return {
|
||
status: 400,
|
||
data: false,
|
||
error: new UniError('不支持的批量操作'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<boolean>
|
||
}
|
||
|
||
return {
|
||
status: 200,
|
||
data: true,
|
||
error: null,
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<boolean>
|
||
} catch (error) {
|
||
return {
|
||
status: 500,
|
||
data: false,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '批量操作失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<boolean>
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取消息统计信息
|
||
*/
|
||
static async getMessageStats(userId : string) : Promise<AkReqResponse<MessageStats>> {
|
||
try {
|
||
// 验证参数不能为空
|
||
if (userId == null || userId === '' || userId.trim() === '') {
|
||
return createErrorResponse<MessageStats>(
|
||
{
|
||
total_messages: 0,
|
||
unread_messages: 0,
|
||
sent_messages: 0,
|
||
received_messages: 0,
|
||
urgent_messages: 0,
|
||
draft_messages: 0
|
||
},
|
||
'参数错误:用户ID不能为空'
|
||
)
|
||
}
|
||
|
||
// 获取未读消息
|
||
const unreadResponse = await supa
|
||
.from('ak_message_recipients')
|
||
.select('id', { count: 'exact' })
|
||
.eq('recipient_id', userId)
|
||
.eq('status', 'unread')
|
||
.eq('is_deleted', false)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
|
||
// 获取总消息数
|
||
const totalResponse = await supa
|
||
.from('ak_message_recipients')
|
||
.select('id', { count: 'exact' })
|
||
.eq('recipient_id', userId)
|
||
.eq('is_deleted', false)
|
||
.executeAs<Array<MessageRecipient>>()
|
||
|
||
// 获取紧急消息数
|
||
const urgentResponse = await supa
|
||
.from('ak_messages')
|
||
.select('id', { count: 'exact' })
|
||
.eq('receiver_id', userId)
|
||
.eq('is_urgent', true)
|
||
.eq('is_deleted', false)
|
||
.executeAs<Array<Message>>()
|
||
const stats : MessageStats = {
|
||
total_messages: Array.isArray(totalResponse.data) ? (totalResponse.data as Array<any>).length : 0,
|
||
unread_messages: Array.isArray(unreadResponse.data) ? (unreadResponse.data as Array<any>).length : 0,
|
||
urgent_messages: Array.isArray(urgentResponse.data) ? (urgentResponse.data as Array<any>).length : 0,
|
||
sent_messages: 0, // 可根据业务补充
|
||
received_messages: 0, // 可根据业务补充
|
||
draft_messages: 0 // 可根据业务补充
|
||
}
|
||
|
||
return {
|
||
status: 200,
|
||
data: stats,
|
||
error: null,
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<MessageStats>
|
||
} catch (error) {
|
||
return {
|
||
status: 500,
|
||
data: null,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '获取消息统计失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<MessageStats>
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 搜索用户(用于消息接收者选择)
|
||
*/
|
||
static async searchUsers(keyword : string) : Promise<AkReqResponse<Array<UserOption>>> {
|
||
try {
|
||
const response = await supa
|
||
.from('ak_users')
|
||
.select('id, username, nickname, avatar', { count: 'exact' })
|
||
.or(`username.ilike.%${keyword}%,nickname.ilike.%${keyword}%`)
|
||
.limit(20)
|
||
.executeAs<Array<UserOption>>()
|
||
|
||
return response
|
||
} catch (error) { return {
|
||
status: 500,
|
||
data: [] as Array<UserOption>,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '搜索失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<Array<UserOption>>
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取群组列表
|
||
*/
|
||
static async getGroups() : Promise<AkReqResponse<Array<MessageGroup>>> {
|
||
try {
|
||
const response = await supa
|
||
.from('ak_message_groups')
|
||
.select('*', {})
|
||
.eq('is_active', true)
|
||
.executeAs<Array<MessageGroup>>()
|
||
return response
|
||
} catch (error) { return {
|
||
status: 500,
|
||
data: [] as Array<MessageGroup>,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '获取群组失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<Array<MessageGroup>>
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取群组成员
|
||
*/
|
||
static async getGroupMembers(groupId : string) : Promise<AkReqResponse<Array<GroupMember>>> {
|
||
try {
|
||
const response = await supa
|
||
.from('ak_message_group_members')
|
||
.select('*', {})
|
||
.eq('group_id', groupId)
|
||
.executeAs<Array<GroupMember>>()
|
||
return response
|
||
} catch (error) { return {
|
||
status: 500,
|
||
data: [] as Array<GroupMember>,
|
||
error: new UniError(typeof error === 'string' ? error : error?.message ?? '获取群组成员失败'),
|
||
origin: null,
|
||
headers: {}
|
||
} as AkReqResponse<Array<GroupMember>>
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建标准错误响应
|
||
*/
|
||
function createErrorResponse<T>(data : T, errorMessage : string, status : number = 500) : AkReqResponse<T> {
|
||
return {
|
||
status: status,
|
||
data: data,
|
||
headers: {},
|
||
error: new UniError(errorMessage),
|
||
total: null,
|
||
page: null,
|
||
limit: null,
|
||
hasmore: false,
|
||
origin: null
|
||
}
|
||
} |