feat(aihr): add M1 broadcast targeting and mandatory reads
This commit is contained in:
@@ -39,7 +39,13 @@ const pending: PendingBroadcastPublish = {
|
||||
tenantId: 'tenant-a',
|
||||
requestId: 'request-1',
|
||||
title: '夏季服务标准通知',
|
||||
content: '请按最新标准执行。'
|
||||
content: '请按最新标准执行。',
|
||||
requiredRead: true,
|
||||
targets: {
|
||||
projectCodes: ['PRJ-FCW'],
|
||||
positionNames: ['物业管家'],
|
||||
positionLevels: []
|
||||
}
|
||||
};
|
||||
|
||||
describe('pending broadcast publish storage', () => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { BroadcastTargetRequest } from '@/api/aihr/broadcast';
|
||||
|
||||
export const pendingBroadcastStoragePrefix = 'aihr.broadcast.pending.v2';
|
||||
|
||||
export interface PendingBroadcastPublish {
|
||||
@@ -5,6 +7,8 @@ export interface PendingBroadcastPublish {
|
||||
requestId: string;
|
||||
title: string;
|
||||
content: string;
|
||||
requiredRead: boolean;
|
||||
targets: BroadcastTargetRequest;
|
||||
}
|
||||
|
||||
export interface BroadcastPendingStorageOptions {
|
||||
@@ -14,6 +18,31 @@ export interface BroadcastPendingStorageOptions {
|
||||
|
||||
const accountKey = (userId: string | number) => String(userId).trim();
|
||||
|
||||
const emptyTargets = (): BroadcastTargetRequest => ({
|
||||
projectCodes: [],
|
||||
positionNames: [],
|
||||
positionLevels: []
|
||||
});
|
||||
|
||||
const parseTargetValues = (value: unknown, maximumCount: number, maximumLength: number) => {
|
||||
if (!Array.isArray(value) || value.length > maximumCount) return undefined;
|
||||
const normalized = value.map((entry) => typeof entry === 'string' ? entry.trim() : '');
|
||||
if (normalized.some((entry) => !entry || entry.length > maximumLength)) return undefined;
|
||||
return [...new Set(normalized)];
|
||||
};
|
||||
|
||||
const parseTargets = (value: unknown): BroadcastTargetRequest | undefined => {
|
||||
if (value === undefined) return emptyTargets();
|
||||
if (!value || typeof value !== 'object') return undefined;
|
||||
const candidate = value as Partial<BroadcastTargetRequest>;
|
||||
const projectCodes = parseTargetValues(candidate.projectCodes, 50, 50);
|
||||
const positionNames = parseTargetValues(candidate.positionNames, 50, 100);
|
||||
const positionLevels = parseTargetValues(candidate.positionLevels, 20, 30);
|
||||
return projectCodes && positionNames && positionLevels
|
||||
? { projectCodes, positionNames, positionLevels }
|
||||
: undefined;
|
||||
};
|
||||
|
||||
export const broadcastPendingStorageKey = (tenantId: string, userId: string | number) => {
|
||||
const normalizedTenantId = String(tenantId).trim();
|
||||
const normalizedUserId = accountKey(userId);
|
||||
@@ -26,11 +55,15 @@ const parsePendingPublish = (raw: string | null): PendingBroadcastPublish | unde
|
||||
if (!raw) return undefined;
|
||||
try {
|
||||
const candidate = JSON.parse(raw) as Partial<PendingBroadcastPublish>;
|
||||
const targets = parseTargets(candidate.targets);
|
||||
const requiredRead = candidate.requiredRead === undefined ? false : candidate.requiredRead;
|
||||
if (
|
||||
typeof candidate.tenantId !== 'string'
|
||||
|| typeof candidate.requestId !== 'string'
|
||||
|| typeof candidate.title !== 'string'
|
||||
|| typeof candidate.content !== 'string'
|
||||
|| typeof requiredRead !== 'boolean'
|
||||
|| !targets
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -38,7 +71,9 @@ const parsePendingPublish = (raw: string | null): PendingBroadcastPublish | unde
|
||||
tenantId: candidate.tenantId,
|
||||
requestId: candidate.requestId,
|
||||
title: candidate.title,
|
||||
content: candidate.content
|
||||
content: candidate.content,
|
||||
requiredRead,
|
||||
targets
|
||||
};
|
||||
} catch {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user