v6:名册联系信息对齐 vCard + 头像与裁剪四项修正
- ① 头像上传失败(超限/非图片)不再挡保存,改为 flash 提示「已加进名册。头像没有保存:…」 ——这是「添加不了」的真根因(错误被塞进 errors.avatar 把整条新增打回) - ② 表单顺序:生日整块下移(身份证能读出生日时无需常改) - ③ 裁剪缩放语义反转:框在屏幕上的位置与大小不动、动它底下的图; 落库归一化矩形随倍率缩小(放大=同一块区域看得更清楚) - ④ 取消应用层 5MB 兆数上限;按像素动态上调 memory_limit(px*6+64M,封顶 768M); 头像输出恒为 512x512 - ⑤ 成员信息对齐 vCard:person_contacts 表(phone/email/address/url 可多行, value 加密落库)+ persons.org(工作单位)/title(职务职称);均可留空 Schema v5→v6:person_contacts 建表 + persons.org/title 加列
This commit is contained in:
+64
-4
@@ -127,9 +127,9 @@ final class Person
|
||||
$sql = 'INSERT INTO persons
|
||||
(family_id, kind, name, gender, birth_rank, birthday,
|
||||
birthday_id_card, birthday_lunar, birthday_lunar_leap, slogan, avatar,
|
||||
phone_hash, phone_encrypted, id_card_encrypted, email, address, note,
|
||||
phone_hash, phone_encrypted, id_card_encrypted, email, address, org, title, note,
|
||||
is_placeholder)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';
|
||||
$st = Database::get()->prepare($sql);
|
||||
$st->execute([
|
||||
$familyId,
|
||||
@@ -148,6 +148,8 @@ final class Person
|
||||
($d['id_card'] ?? '') !== '' ? Crypto::encrypt($d['id_card']) : null,
|
||||
($d['email'] ?? '') !== '' ? $d['email'] : null,
|
||||
($d['address'] ?? '') !== '' ? $d['address'] : null,
|
||||
($d['org'] ?? '') !== '' ? mb_substr(trim((string)$d['org']), 0, 120, 'UTF-8') : null,
|
||||
($d['title'] ?? '') !== '' ? mb_substr(trim((string)$d['title']), 0, 120, 'UTF-8') : null,
|
||||
($d['note'] ?? '') !== '' ? $d['note'] : null,
|
||||
!empty($d['is_placeholder']) ? 1 : 0,
|
||||
]);
|
||||
@@ -162,7 +164,7 @@ final class Person
|
||||
name = ?, gender = ?, birth_rank = ?, birthday = ?,
|
||||
birthday_id_card = ?, birthday_lunar = ?, birthday_lunar_leap = ?, slogan = ?, avatar = ?,
|
||||
phone_hash = ?, phone_encrypted = ?, id_card_encrypted = ?,
|
||||
email = ?, address = ?, note = ?, is_placeholder = ?
|
||||
email = ?, address = ?, org = ?, title = ?, note = ?, is_placeholder = ?
|
||||
WHERE id = ?';
|
||||
Database::get()->prepare($sql)->execute([
|
||||
$d['name'],
|
||||
@@ -179,6 +181,8 @@ final class Person
|
||||
($d['id_card'] ?? '') !== '' ? Crypto::encrypt($d['id_card']) : null,
|
||||
($d['email'] ?? '') !== '' ? $d['email'] : null,
|
||||
($d['address'] ?? '') !== '' ? $d['address'] : null,
|
||||
($d['org'] ?? '') !== '' ? mb_substr(trim((string)$d['org']), 0, 120, 'UTF-8') : null,
|
||||
($d['title'] ?? '') !== '' ? mb_substr(trim((string)$d['title']), 0, 120, 'UTF-8') : null,
|
||||
($d['note'] ?? '') !== '' ? $d['note'] : null,
|
||||
!empty($d['is_placeholder']) ? 1 : 0,
|
||||
$id,
|
||||
@@ -202,7 +206,7 @@ final class Person
|
||||
'UPDATE persons SET
|
||||
name = ?, gender = ?, birthday = ?,
|
||||
birthday_id_card = ?, birthday_lunar = ?, birthday_lunar_leap = ?, slogan = ?, avatar = ?,
|
||||
email = ?, address = ?, note = ?
|
||||
email = ?, address = ?, org = ?, title = ?, note = ?
|
||||
WHERE id = ?'
|
||||
)->execute([
|
||||
$d['name'],
|
||||
@@ -215,11 +219,67 @@ final class Person
|
||||
($d['avatar'] ?? '') !== '' ? $d['avatar'] : null,
|
||||
($d['email'] ?? '') !== '' ? $d['email'] : null,
|
||||
($d['address'] ?? '') !== '' ? $d['address'] : null,
|
||||
($d['org'] ?? '') !== '' ? mb_substr(trim((string)$d['org']), 0, 120, 'UTF-8') : null,
|
||||
($d['title'] ?? '') !== '' ? mb_substr(trim((string)$d['title']), 0, 120, 'UTF-8') : null,
|
||||
($d['note'] ?? '') !== '' ? $d['note'] : null,
|
||||
$id,
|
||||
]);
|
||||
}
|
||||
|
||||
// ---------------- 补充联系方式(vCard 对齐,v6 起) ----------------
|
||||
//
|
||||
// 「主手机号」不在这里:它是登录凭据(phone_hash 唯一约束、按它找人),
|
||||
// 永远留在 persons.phone_*。contacts 存的是**额外的**电话 / 邮箱 / 地址 / 网址。
|
||||
// 电话与主手机号同一把钥匙加密存(value 是密文),展示时用 Crypto::maskPhone 打码。
|
||||
|
||||
/** 某个人的全部补充联系方式,按 kind 分组;kind=phone 的 value 是**密文** */
|
||||
public static function contactsOf(int $personId): array
|
||||
{
|
||||
$st = Database::get()->prepare(
|
||||
'SELECT kind, label, value FROM person_contacts
|
||||
WHERE person_id = ? ORDER BY kind, position, id'
|
||||
);
|
||||
$st->execute([$personId]);
|
||||
$out = ['phone' => [], 'email' => [], 'address' => [], 'url' => []];
|
||||
foreach ($st->fetchAll() as $r) {
|
||||
$out[(string)$r['kind']][] = ['label' => (string)$r['label'], 'value' => (string)$r['value']];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 整组替换一个人的补充联系方式(delete + insert,幂等且不留下悬旧行)。
|
||||
*
|
||||
* @param string[][][] $byKind kind => 行列表,每行 ['label' => .., 'value' => ..]
|
||||
* value 一律传**明文**;kind=phone 在这里统一加密。
|
||||
* value 为空的行直接丢弃(表单会带出一排空行)。
|
||||
*/
|
||||
public static function replaceContacts(int $personId, array $byKind): void
|
||||
{
|
||||
$db = Database::get();
|
||||
$ins = $db->prepare(
|
||||
'INSERT INTO person_contacts (person_id, kind, label, value, position) VALUES (?,?,?,?,?)'
|
||||
);
|
||||
$db->prepare('DELETE FROM person_contacts WHERE person_id = ?')->execute([$personId]);
|
||||
foreach (['phone', 'email', 'address', 'url'] as $kind) {
|
||||
$pos = 100;
|
||||
foreach (($byKind[$kind] ?? []) as $row) {
|
||||
$value = trim((string)($row['value'] ?? ''));
|
||||
if ($value === '') {
|
||||
continue; // 空行是表单带出来的占位,不是数据
|
||||
}
|
||||
$label = mb_substr(trim((string)($row['label'] ?? '')), 0, 20, 'UTF-8');
|
||||
if ($kind === 'phone') {
|
||||
$value = Crypto::encrypt(mb_substr($value, 0, 60, 'UTF-8'));
|
||||
} else {
|
||||
$value = mb_substr($value, 0, 500, 'UTF-8');
|
||||
}
|
||||
$ins->execute([$personId, $kind, $label, $value, $pos]);
|
||||
$pos += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 只改头像(上传后单独调用,避免整条记录被表单覆盖) */
|
||||
public static function setAvatar(int $id, ?string $avatar): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user