feat(ECR-012): 星座对齐收口,并 Closed ECR-007/008
对齐 outlook/分享 type=star/报告页运势面板与测试;流程上关闭 Ops-B/C 两张 ECR。真支付仍后置。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -135,7 +135,27 @@ export function signCardsFrom(summary: Record<string, unknown>): SignCard[] {
|
||||
|
||||
export function fortuneBundleFrom(summary: Record<string, unknown>): Record<string, FortunePeriod> {
|
||||
const o = summary.outlook
|
||||
return o && typeof o === 'object' ? (o as Record<string, FortunePeriod>) : {}
|
||||
if (!o || typeof o !== 'object') return {}
|
||||
const src = o as Record<string, unknown>
|
||||
const out: Record<string, FortunePeriod> = {}
|
||||
for (const k of reportFortuneKeys) {
|
||||
const raw = src[k]
|
||||
if (raw && typeof raw === 'object') out[k] = periodFromRaw(raw as Record<string, unknown>)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function periodFromRaw(p: Record<string, unknown>): FortunePeriod {
|
||||
return {
|
||||
title: typeof p.title === 'string' ? p.title : undefined,
|
||||
label: typeof p.label === 'string' ? p.label : undefined,
|
||||
score: typeof p.score === 'number' ? p.score : undefined,
|
||||
tip: typeof p.tip === 'string' ? p.tip : undefined,
|
||||
focus: typeof p.focus === 'string' ? p.focus : undefined,
|
||||
boost: typeof p.boost === 'string' ? p.boost : undefined,
|
||||
caution: typeof p.caution === 'string' ? p.caution : undefined,
|
||||
dims: p.dims && typeof p.dims === 'object' ? (p.dims as FortunePeriod['dims']) : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export function planetsFrom(summary: Record<string, unknown>): PlanetRow[] {
|
||||
@@ -172,6 +192,9 @@ export function buildReportSharePayload(
|
||||
keywords,
|
||||
} as RelationSharePayload
|
||||
}
|
||||
if (report.type === 'star') {
|
||||
return { type: 'star', title: headline, line: oneLiner, keywords }
|
||||
}
|
||||
return {
|
||||
type: 'portrait',
|
||||
title: headline,
|
||||
|
||||
@@ -39,8 +39,25 @@ describe('shareLink', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('round-trips star payload', () => {
|
||||
const path = buildSharePath({
|
||||
type: 'star',
|
||||
title: '稳健沉淀者',
|
||||
line: '星座一句',
|
||||
keywords: ['金牛'],
|
||||
})
|
||||
const q = Object.fromEntries(new URLSearchParams(path.slice(path.indexOf('?'))))
|
||||
expect(parseShareQuery(q)).toEqual({
|
||||
type: 'star',
|
||||
title: '稳健沉淀者',
|
||||
line: '星座一句',
|
||||
keywords: ['金牛'],
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null for empty/invalid', () => {
|
||||
expect(parseShareQuery({})).toBeNull()
|
||||
expect(parseShareQuery({ type: 'portrait' })).toBeNull()
|
||||
expect(parseShareQuery({ type: 'star' })).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,14 @@ export type PortraitSharePayload = {
|
||||
keywords: string[]
|
||||
}
|
||||
|
||||
/** Same card shape as portrait; type discriminates 星座 landing copy. */
|
||||
export type StarSharePayload = {
|
||||
type: 'star'
|
||||
title: string
|
||||
line: string
|
||||
keywords: string[]
|
||||
}
|
||||
|
||||
export type RelationSharePayload = {
|
||||
type: 'relation'
|
||||
me: string
|
||||
@@ -15,15 +23,22 @@ export type RelationSharePayload = {
|
||||
keywords: string[]
|
||||
}
|
||||
|
||||
export type SharePayload = PortraitSharePayload | RelationSharePayload
|
||||
export type SharePayload = PortraitSharePayload | StarSharePayload | RelationSharePayload
|
||||
|
||||
function appendTitleLineKeywords(
|
||||
q: URLSearchParams,
|
||||
payload: PortraitSharePayload | StarSharePayload,
|
||||
) {
|
||||
if (payload.title) q.set('t', payload.title)
|
||||
if (payload.line) q.set('l', payload.line)
|
||||
if (payload.keywords.length) q.set('k', payload.keywords.slice(0, 6).join(','))
|
||||
}
|
||||
|
||||
export function buildSharePath(payload: SharePayload): string {
|
||||
const q = new URLSearchParams()
|
||||
q.set('type', payload.type)
|
||||
if (payload.type === 'portrait') {
|
||||
if (payload.title) q.set('t', payload.title)
|
||||
if (payload.line) q.set('l', payload.line)
|
||||
if (payload.keywords.length) q.set('k', payload.keywords.slice(0, 6).join(','))
|
||||
if (payload.type === 'portrait' || payload.type === 'star') {
|
||||
appendTitleLineKeywords(q, payload)
|
||||
} else {
|
||||
if (payload.me) q.set('me', payload.me)
|
||||
if (payload.other) q.set('ot', payload.other)
|
||||
@@ -39,11 +54,11 @@ export function parseShareQuery(query: Record<string, unknown>): SharePayload |
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
if (type === 'portrait') {
|
||||
if (type === 'portrait' || type === 'star') {
|
||||
const title = String(query.t || '')
|
||||
const line = String(query.l || '')
|
||||
if (!title && !line && !keywords.length) return null
|
||||
return { type: 'portrait', title, line, keywords }
|
||||
return { type: type as 'portrait' | 'star', title, line, keywords }
|
||||
}
|
||||
if (type === 'relation') {
|
||||
const me = String(query.me || '')
|
||||
|
||||
Reference in New Issue
Block a user