feat: 管理端与用户端批量更新(订单/邀请双码/tabBar/审核与分销等)

- 管理端:用户详情、企业看板双小程序码、超管设置与用户列表等
- 后端:订单接口、邀请码企业版+个人版、分析与测试相关调整
- 微信小程序/抖音:自定义 tabBar 居中与拍摄钮上移、相机页与结果页、订单页、支付与统计
- 新增 faceResultDetail 工具与 mp 分析迁移脚本

Made-with: Cursor
This commit is contained in:
Ghost
2026-03-28 16:46:00 +08:00
parent 2bb8b979f1
commit 3317c84b5e
65 changed files with 4442 additions and 1839 deletions

View File

@@ -0,0 +1,138 @@
/**
* 人脸 / AI 分析结果:与微信小程序 pages/index/result 展示字段对齐,供后台「测试详情」使用
*/
export interface FaceFeatureItem {
label: string
description: string
}
export interface FaceBoneIceSummary {
elementType?: string
boneFleshRelation?: string
}
export interface FaceDetailView {
photos: string[]
mbti: { type?: string; title?: string } | null
disc: { primary?: string; secondary?: string } | null
pdp: { primary?: string; secondary?: string } | null
overview: string
personalitySummary: string
/** 字符串版面相(与小程序 faceAnalysisText */
faceAnalysisText: string
/** 结构化面相:面部特征列表 */
faceFeatures: FaceFeatureItem[]
/** 字符串版骨相正文 */
boneAnalysisText: string
/** 《冰鉴》结构化摘要(小程序 boneFormSummary */
boneIceSummary: FaceBoneIceSummary | null
advantages: string[]
gallupTop3: string[]
relationship: string
careerDevelopment: string
familyParenting: string
partnerCofounder: string
portrait: Record<string, any> | null
hrView: Record<string, any> | null
bossView: Record<string, any> | null
resumeHighlights: string
careers: string[]
}
function parseMaybeJsonObject(raw: unknown): Record<string, any> | null {
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
return raw as Record<string, any>
}
if (typeof raw === 'string' && raw.trim().startsWith('{')) {
try {
const o = JSON.parse(raw) as unknown
return o && typeof o === 'object' && !Array.isArray(o) ? (o as Record<string, any>) : null
} catch {
return null
}
}
return null
}
function extractFaceFeatures(raw: unknown): FaceFeatureItem[] {
const o = parseMaybeJsonObject(raw)
if (!o) return []
const fe = o.facialFeatures
if (!Array.isArray(fe)) return []
return fe
.map((item: any) => ({
label: String(item?.label ?? ''),
description: String(item?.description ?? '')
}))
.filter((f) => f.label || f.description)
}
function extractBoneIceSummary(raw: unknown): FaceBoneIceSummary | null {
const o = parseMaybeJsonObject(raw)
const sum = o?.boneFormSummary
if (sum && typeof sum === 'object') {
return {
elementType: sum.elementType != null ? String(sum.elementType) : undefined,
boneFleshRelation: sum.boneFleshRelation != null ? String(sum.boneFleshRelation) : undefined
}
}
return null
}
/** 从 AI 结果 JSON 构建后台展示模型(兼容字符串 / 结构化 / JSON 字符串) */
export function buildFaceDetailFromParsed(parsed: Record<string, any> | null | undefined): FaceDetailView | null {
if (!parsed || typeof parsed !== 'object') return null
const faceRaw = parsed.faceAnalysis
const boneRaw = parsed.boneAnalysis
const faceFeatures = extractFaceFeatures(faceRaw)
let faceAnalysisText = ''
if (typeof faceRaw === 'string') {
const tryObj = parseMaybeJsonObject(faceRaw)
if (tryObj && Array.isArray(tryObj.facialFeatures) && tryObj.facialFeatures.length) {
faceAnalysisText = ''
} else {
faceAnalysisText = faceRaw.trim()
}
}
const boneIceSummary = extractBoneIceSummary(boneRaw)
let boneAnalysisText = ''
if (typeof boneRaw === 'string') {
const tryObj = parseMaybeJsonObject(boneRaw)
if (tryObj?.boneFormSummary && typeof tryObj.boneFormSummary === 'object') {
boneAnalysisText = ''
} else {
boneAnalysisText = boneRaw.trim()
}
}
const strArr = (v: unknown): string[] =>
Array.isArray(v) ? (v as unknown[]).map((x) => String(x)).filter(Boolean) : []
return {
photos: Array.isArray(parsed.photoUrls) ? (parsed.photoUrls as string[]) : [],
mbti: parsed.mbti && typeof parsed.mbti === 'object' ? parsed.mbti : null,
disc: parsed.disc && typeof parsed.disc === 'object' ? parsed.disc : null,
pdp: parsed.pdp && typeof parsed.pdp === 'object' ? parsed.pdp : null,
overview: String(parsed.overview ?? ''),
personalitySummary: String(parsed.personalitySummary ?? ''),
faceAnalysisText,
faceFeatures,
boneAnalysisText,
boneIceSummary,
advantages: strArr(parsed.advantages),
gallupTop3: strArr(parsed.gallupTop3),
relationship: String(parsed.relationship ?? ''),
careerDevelopment: String(parsed.careerDevelopment ?? ''),
familyParenting: String(parsed.familyParenting ?? ''),
partnerCofounder: String(parsed.partnerCofounder ?? ''),
portrait: parsed.portrait && typeof parsed.portrait === 'object' ? parsed.portrait : null,
hrView: parsed.hrView && typeof parsed.hrView === 'object' ? parsed.hrView : null,
bossView: parsed.bossView && typeof parsed.bossView === 'object' ? parsed.bossView : null,
resumeHighlights: String(parsed.resumeHighlights ?? ''),
careers: strArr(parsed.careers)
}
}