- assignOrgs 校验 orgCodes 存在性(对齐 assignResources) - lock/kick 保护保留角色账号(super_admin/tenant_super_admin) - BatchDeleteDto 加 @ArrayMaxSize(100) - 待改密门禁显式放行 /auth/logout - 分配部门改用独立权限码 role:assign-org Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
85 lines
3.2 KiB
TypeScript
85 lines
3.2 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
||
import { BaseCrudService } from '../common/base-crud.service.js';
|
||
import { PrismaService } from '../prisma/prisma.service.js';
|
||
import { getRequestContext } from '../common/context/request-context.js';
|
||
import { CreateNoticeDto, ListNoticeQuery, UpdateNoticeDto } from './dto.js';
|
||
|
||
@Injectable()
|
||
export class NoticesService extends BaseCrudService {
|
||
constructor(prisma: PrismaService) {
|
||
super(prisma, 'notice');
|
||
}
|
||
|
||
listNotices(q: ListNoticeQuery) {
|
||
const where: Record<string, unknown> = {};
|
||
if (q.title) where.title = { contains: q.title };
|
||
if (q.status) where.status = q.status;
|
||
return this.paginate(q.page, q.pageSize, where);
|
||
}
|
||
|
||
createNotice(dto: CreateNoticeDto) {
|
||
return this.create({ ...dto });
|
||
}
|
||
|
||
updateNotice(id: string, dto: UpdateNoticeDto) {
|
||
return this.update(id, { ...dto });
|
||
}
|
||
|
||
/** 当前用户可见通知(enabled)+ 已读状态。 */
|
||
async mine(q: ListNoticeQuery) {
|
||
const ctx = getRequestContext()!;
|
||
const [rows, total] = await Promise.all([
|
||
this.prisma.client.notice.findMany({
|
||
where: { status: 'enabled' },
|
||
skip: (q.page - 1) * q.pageSize,
|
||
take: q.pageSize,
|
||
orderBy: { createdAt: 'desc' },
|
||
select: { id: true, title: true, content: true, status: true, createdAt: true },
|
||
}),
|
||
this.prisma.client.notice.count({ where: { status: 'enabled' } }),
|
||
]);
|
||
const ids = rows.map((r) => r.id);
|
||
const readRows = ids.length
|
||
? await this.prisma.client.noticeRead.findMany({
|
||
where: { userId: ctx.userId, noticeId: { in: ids } },
|
||
select: { noticeId: true, readAt: true },
|
||
})
|
||
: [];
|
||
const readMap = new Map(readRows.map((r) => [r.noticeId, r.readAt]));
|
||
const list = rows.map((r) => ({ ...r, readAt: readMap.get(r.id) ?? null }));
|
||
return { list, total, page: q.page, pageSize: q.pageSize };
|
||
}
|
||
|
||
/** 未读数 = enabled 通知数 − 当前用户已读数(O(enabled 通知数),公告量小可接受)。 */
|
||
async unreadCount(): Promise<number> {
|
||
const ctx = getRequestContext()!;
|
||
const enabled = await this.prisma.client.notice.findMany({ where: { status: 'enabled' }, select: { id: true } });
|
||
const enabledIds = enabled.map((n) => n.id);
|
||
const readCount = enabledIds.length
|
||
? await this.prisma.client.noticeRead.count({ where: { userId: ctx.userId, noticeId: { in: enabledIds } } })
|
||
: 0;
|
||
return Math.max(0, enabled.length - readCount);
|
||
}
|
||
|
||
async read(id: string): Promise<null> {
|
||
const ctx = getRequestContext()!;
|
||
await this.prisma.client.noticeRead.upsert({
|
||
where: { tenantCode_noticeId_userId: { tenantCode: ctx.tenantCode, noticeId: id, userId: ctx.userId } },
|
||
update: {},
|
||
create: { tenantCode: ctx.tenantCode, noticeId: id, userId: ctx.userId },
|
||
});
|
||
return null;
|
||
}
|
||
|
||
async readAll(): Promise<null> {
|
||
const ctx = getRequestContext()!;
|
||
const enabled = await this.prisma.client.notice.findMany({ where: { status: 'enabled' }, select: { id: true } });
|
||
if (enabled.length === 0) return null;
|
||
await this.prisma.client.noticeRead.createMany({
|
||
data: enabled.map((n) => ({ tenantCode: ctx.tenantCode, noticeId: n.id, userId: ctx.userId })),
|
||
skipDuplicates: true,
|
||
});
|
||
return null;
|
||
}
|
||
}
|