diff --git a/src/pages/users/shareCard.wpy b/src/pages/users/shareCard.wpy
index 7d09ebd..bb774ff 100644
--- a/src/pages/users/shareCard.wpy
+++ b/src/pages/users/shareCard.wpy
@@ -9,7 +9,7 @@
- 福恋期待认真相识{{profile.nickname || '我的卡片'}}{{profile.details}}{{profile.introduction || '上福恋认真找对象'}}
+ 福恋期待认真相识{{profile.nickname || '我的卡片'}}{{profile.details}}{{profile.introduction || '上福恋认真找对象'}}
{{profileLoading ? '正在读取个人资料' : posterStage}}
{{posterError || profileError}}
diff --git a/src/utils/sharing-cache.js b/src/utils/sharing-cache.js
index 597ab88..64e1081 100644
--- a/src/utils/sharing-cache.js
+++ b/src/utils/sharing-cache.js
@@ -1,6 +1,6 @@
import {boundedCall, fileAccess} from './sharing-runtime'
-const VERSION = 'v5'
+const VERSION = 'v6'
const MAX_AGE = 24 * 60 * 60 * 1000
const INDEX = 'fulian-sharing-cache-' + VERSION
diff --git a/src/utils/sharing.js b/src/utils/sharing.js
index b63af39..e088fd4 100644
--- a/src/utils/sharing.js
+++ b/src/utils/sharing.js
@@ -34,10 +34,18 @@ function textLines(ctx,text,x,y,width,lineHeight,limit) {
}
ctx.fillText(line,x,y+row*lineHeight)
}
+export function photoPlacement(iw,ih,w,h){
+ // Keep the whole source visible: a center cover crop can remove faces,
+ // especially when a portrait/group photo is placed in a landscape slot.
+ const scale=Math.min(w/iw,h/ih)
+ const width=iw*scale,height=ih*scale
+ return {x:(w-width)/2,y:(h-height)/2,width,height}
+}
function photoFill(ctx,image,x,y,w,h,r=0){
ctx.save();if(r){roundRect(ctx,x,y,w,h,r);ctx.clip()}
- const scale=Math.max(w/image.width,h/image.height),sw=w/scale,sh=h/scale
- ctx.drawImage(image,(image.width-sw)/2,(image.height-sh)/2,sw,sh,x,y,w,h);ctx.restore()
+ ctx.fillStyle='#FFF1F4';ctx.fillRect(x,y,w,h)
+ const fit=photoPlacement(image.width,image.height,w,h)
+ ctx.drawImage(image,0,0,image.width,image.height,x+fit.x,y+fit.y,fit.width,fit.height);ctx.restore()
}
async function canvasFor(page,id,width,height,job){
check(job)
diff --git a/tools/sharing/VALIDATION-20261102.md b/tools/sharing/VALIDATION-20261102.md
index 30b86de..a4a6409 100644
--- a/tools/sharing/VALIDATION-20261102.md
+++ b/tools/sharing/VALIDATION-20261102.md
@@ -84,3 +84,11 @@ Node v24.19.0:六组分享回归退出0。check-photo 增加随机排除当前
Node v16.20.2 在 .release-build/invitation-card-candidate 执行 tools/build/build.cjs,退出0,89模板编译完成。改动仍在功能分支,未更新 main 验收包。
2026-11-03 用户确认后端修复生效但线上换图未变化。核实上一交付前端 main f82dab2 仍固定优选,随机换图代码尚在功能分支。本轮补齐前端交付,合并后从 main 编译;多张合格照片排除当前项,缓存保留新选择。
+
+## 2026-11-03 个人照片防裁头适配
+
+用户反馈横向照片框居中裁剪导致头部出框。本轮采用完整照片等比例适配:保留全部源图,按海报/分享封面尺寸自动缩放居中,空余处填充福恋浅玫瑰背景;加载预览也改为 aspectFit。未引入人脸检测,不声称识别人脸;此方案直接避免照片框裁掉源图中的头部,无法恢复原图本身已缺失的内容。缓存升级v6使旧裁剪海报失效。
+
+Node24几何检查覆盖竖图/横图/方图在686×470和264×364目标框内等比例完整显示,通过;check-photo/check-cache/check-image-path退出0,diff检查通过。后端无本轮修改。分支 feature/card-photo-framing,未上传发布。
+
+2026-11-03 用户确认生成正式验收包。照片适配功能分支编译退出0,按授权合并本地 main 后重新编译。制品目录 releases/20261103-card-photo-framing,未上传部署。
diff --git a/tools/sharing/check-cache.cjs b/tools/sharing/check-cache.cjs
index 4004a16..0761eb3 100644
--- a/tools/sharing/check-cache.cjs
+++ b/tools/sharing/check-cache.cjs
@@ -8,7 +8,7 @@ async function render(h,name='shareCard'){const page=h.page(name);page.hook('onR
h.files.delete(changed.cover);const missing=await render(h);assert(missing.poster!==changed.poster,'missing file must regenerate')
h.storage.set('token','synthetic-other-token');h.storage.set('user_id',43);h.state.profile.id=43
const other=await render(h);assert(other.poster.includes('-43-personal-'));assert(!other.poster.includes('-42-personal-'))
- const index=h.storage.get('fulian-sharing-cache-v5');index['43-personal'].savedAt=Date.now()-2*24*3600*1000;h.storage.set('fulian-sharing-cache-v5',index)
+ const index=h.storage.get('fulian-sharing-cache-v6');index['43-personal'].savedAt=Date.now()-2*24*3600*1000;h.storage.set('fulian-sharing-cache-v6',index)
const expired=await render(h);assert(expired.poster!==other.poster)
h.flags.copyFail=true;const disk=await render(h,'myShare');assert(disk.poster&&!disk.cached);assert(!disk.generating)
for(const flag of ['imageInfoHang','imageHang','canvasHang','exportHang','requestHang']){