Files
crawler-plugin/admin-frontend-vue/src/pages/account/shop-group-model.ts
T

104 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 数据权限分组列表模型(任务 59module 13 task 253 扩展编辑能力):解析 VO + 摘要 + 提交载荷。 */
import { unwrap } from '../../api/envelope.ts'
export interface ShopGroupItem {
id: number
name: string
leaderUsername: string
leaderUserId?: number
memberCount: number
memberUsernames: string[]
memberUserIds?: number[]
createdAt?: string
updatedAt?: string
}
export interface ShopGroupFormErrors {
groupName?: string
}
function text(value: unknown): string {
return typeof value === 'string' ? value.trim() : ''
}
function numberOrNull(value: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : null
}
/** 解析单条分组项;缺 id 视为无效。 */
export function parseShopGroupItem(raw: unknown): ShopGroupItem | null {
if (!raw || typeof raw !== 'object') return null
const record = raw as Record<string, unknown>
const id = numberOrNull(record.id)
if (id === null) return null
const names = Array.isArray(record.memberUsernames)
? record.memberUsernames.map((v) => text(v)).filter(Boolean)
: []
const memberIds = Array.isArray(record.memberUserIds)
? record.memberUserIds.map((v) => numberOrNull(v)).filter((v): v is number => v !== null && v > 0)
: []
const count = numberOrNull(record.memberCount)
const item: ShopGroupItem = {
id,
name: text(record.groupName ?? record.name) || `分组${id}`,
leaderUsername: text(record.leaderUsername ?? record.leader_name),
memberCount: count === null ? names.length : count,
memberUsernames: names,
}
const leaderId = numberOrNull(record.leaderUserId ?? record.leader_user_id)
if (leaderId !== null && leaderId > 0) item.leaderUserId = leaderId
if (memberIds.length) item.memberUserIds = memberIds
const createdAt = text(record.createdAt ?? record.created_at)
if (createdAt) item.createdAt = createdAt
const updatedAt = text(record.updatedAt ?? record.updated_at)
if (updatedAt) item.updatedAt = updatedAt
return item
}
/** 归一化分组列表响应(信封或裸数组)。 */
export function parseShopGroupList(payload: unknown): ShopGroupItem[] {
const data = unwrap<unknown>(payload)
if (!Array.isArray(data)) return []
return data
.map((raw) => parseShopGroupItem(raw))
.filter((item): item is ShopGroupItem => item !== null)
}
/** 分组摘要:分组总数 + 组员总数。 */
export function shopGroupSummary(groups: ShopGroupItem[]): { groupCount: number; memberTotal: number } {
let memberTotal = 0
for (const group of groups) memberTotal += group.memberCount || 0
return { groupCount: groups.length, memberTotal }
}
/** 分组名必填校验(镜像后端 NotBlank 文案「请输入分组名称」)。 */
export function validateShopGroupForm(name: string): ShopGroupFormErrors {
const errors: ShopGroupFormErrors = {}
if (!text(name)) errors.groupName = '请输入分组名称'
return errors
}
/** 创建载荷:groupName + 成员 + 创建人(= 组长,当前操作者)。 */
export function toShopGroupCreatePayload(
name: string,
memberUserIds: number[],
createdById: number,
): { groupName: string; memberUserIds: number[]; createdById: number } {
return { groupName: text(name), memberUserIds: dedupeIds(memberUserIds), createdById }
}
/** 更新载荷:groupName + 成员(组长/创建人不可改)。 */
export function toShopGroupUpdatePayload(name: string, memberUserIds: number[]): { groupName: string; memberUserIds: number[] } {
return { groupName: text(name), memberUserIds: dedupeIds(memberUserIds) }
}
function dedupeIds(ids: unknown[]): number[] {
if (!Array.isArray(ids)) return []
const set = new Set<number>()
for (const v of ids) {
const id = numberOrNull(v)
if (id !== null && id > 0) set.add(id)
}
return [...set].sort((a, b) => a - b)
}