export type FinancingDimension = { key: string, label: string, score: number, maximum: number, basis: string } export type FinancingGradeView = { status: string, grade: string, label: string, score: string, weightedScore: string, capacityScore: number, completenessPercent: number, capacityWeight: number, completenessWeight: number, dimensions: FinancingDimension[], limitations: string[], notice: string } // 只读取该版报告保存的等级;旧报告不借用当前规则在客户端补算。 export function financingGradeFrom(raw: UTSJSONObject | null): FinancingGradeView { const empty: FinancingGradeView = { status: 'unavailable', grade: '', label: '本次未评级', score: '', weightedScore: '', capacityScore: 0, completenessPercent: 0, capacityWeight: 80, completenessWeight: 20, dimensions: [], limitations: [], notice: '这份报告未计算综合等级,可重新评估获取。' } if (raw == null) return empty const fail = (): void => { throw new Error('融资等级数据暂不可读取,请重新加载。') } const status = raw.getString('status') ?? '' if (!['evaluated', 'insufficient_data', 'needs_verification', 'policy_unconfirmed'].includes(status) || raw.getString('verificationStatus') != 'not_verified' || (raw.getString('policyVersion') ?? '').length == 0) fail() empty.status = status empty.label = raw.getString('label') ?? '本次未评级' empty.notice = raw.getString('notice') ?? '' const limitations = raw['limitations'] as string[] | null if (limitations == null || !Array.isArray(limitations) || limitations.some((item: string): boolean => typeof item != 'string')) fail() empty.limitations = limitations! if (status != 'evaluated') { if (raw['grade'] != null || raw['score'] != null) fail() return empty } const grade = raw.getString('grade') ?? '' const score = raw.getString('score') ?? '' const weighted = raw.getString('weightedScore') ?? '' const capacity = raw.getNumber('capacityScore'); const complete = raw.getNumber('completenessPercent') const capacityWeight = raw.getNumber('capacityWeight'); const completeWeight = raw.getNumber('completenessWeight') if (!['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-'].includes(grade) || !/^(?:100|[1-9]?[0-9])\.[0-9]{2}$/.test(score) || !/^(?:100|[1-9]?[0-9])\.[0-9]{2}$/.test(weighted) || parseFloat(score) > 100 || parseFloat(weighted) > 100 || parseFloat(score) > parseFloat(weighted) || capacity == null || !Number.isInteger(capacity) || capacity < 0 || capacity > 100 || complete == null || !Number.isInteger(complete) || complete < 0 || complete > 100 || capacityWeight == null || completeWeight == null || !Number.isInteger(capacityWeight) || !Number.isInteger(completeWeight) || capacityWeight < 1 || completeWeight < 1 || capacityWeight + completeWeight != 100) fail() const dimensions = raw['dimensions'] as UTSJSONObject[] | null if (dimensions == null || !Array.isArray(dimensions) || dimensions.length != 5) fail() const keys: string[] = [] let sum = 0; let maximumSum = 0 empty.dimensions = dimensions!.map((item: UTSJSONObject): FinancingDimension => { const key = item.getString('key') ?? ''; const points = item.getNumber('score'); const maximum = item.getNumber('maximum') if (!['repayment', 'debt', 'income', 'supplement', 'assets'].includes(key) || keys.includes(key) || points == null || maximum == null || !Number.isInteger(points) || !Number.isInteger(maximum) || points < 0 || points > maximum || maximum < 1) fail() keys.push(key); sum += points!; maximumSum += maximum! return { key, label: item.getString('label') ?? '', score: points!, maximum: maximum!, basis: item.getString('basis') ?? '' } }) if (sum != capacity || maximumSum != 100) fail() empty.grade = grade; empty.score = score; empty.weightedScore = weighted; empty.capacityScore = capacity!; empty.completenessPercent = complete!; empty.capacityWeight = capacityWeight!; empty.completenessWeight = completeWeight! return empty }