v24 第 9 批:名册九条(分组按钮/加入名册/生日月日/地址四级/千人性能/拖动修复/删除人员/返回名册/Semi DatePicker)
- #131 名册卡片加「分组」按钮(details+radio,显示当前分组,选中即改) - #132 修复「从已有人里加入名册」无反应 - #133 生日只显示月和日(阳历 MM-DD / 农历中文名),PersonQuery::birthdaysOf 取代 birthdayOf - #134 地址结构化为 国家/省/市/区+详细:regions 表(3429 行全国区划)+ Region.php + ?r=regions 级联接口 + 表单 .addr-grid + 老地址回填(Schema v16) - #135 千人名册性能:.person content-visibility:auto + 名册页 ob_gzhandler (实测 服务端 1.15s/3.8MB → 0.17s/87KB;客户端导航→load 1.35s → 0.37s) - #136 拖动排序改 Pointer Events(Chrome 下 HTML5 DnD 在 DOM 变动时断) - #137 个人详情页底部「删除人员」 - #138 编辑资料页加「返回名册」 - #139 所有日期选择换 Semi Design DatePicker(渐进增强:原生四字段保 POST 契约, 懒加载 vendor semi UMD,值回写原字段;工作经历 type=month 一并升级;宣纸主题对齐) 验证:e2e 785/0、batch9 37/0、upgrade 126/0、picker 28/0、mobile 688/0、interact 97/0、 batch_e 91/0、import 21/0、douban 22/0、drag 5/0、semi-integrate 28/0、semi-probe 17/0、 geom 5/5;部署后线上自检 16/16(含 4 个 semi 资源 200 + regions 路由)。
This commit is contained in:
+79
-1
@@ -71,7 +71,7 @@ final class Schema
|
||||
* ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。
|
||||
* 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。
|
||||
*/
|
||||
public const VERSION = '15';
|
||||
public const VERSION = '16';
|
||||
|
||||
/**
|
||||
* 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。
|
||||
@@ -604,6 +604,19 @@ CREATE TABLE IF NOT EXISTS `task_comments` (
|
||||
CONSTRAINT `fk_tc_task` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_tc_person` FOREIGN KEY (`person_id`) REFERENCES `persons` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务评论(支持 @人)'
|
||||
SQL,
|
||||
<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS `regions` (
|
||||
/* ⚠️ VARCHAR(12) 不是 6:东莞、中山这类「市直管镇」的城市,数据集里第四级
|
||||
(镇 / 街道)是 9 位代码 —— 写 6 会让插入在第 2170 行被截断裂掉,
|
||||
后面一千多条全丢(实测只剩 19 个省)。 */
|
||||
`code` VARCHAR(12) NOT NULL COMMENT '行政区划代码:省 2 位 / 市 4 位 / 区县 6 位(市直管镇的镇 9 位)',
|
||||
`name` VARCHAR(60) NOT NULL COMMENT '标准名称,如「北京市」「青岛市」「市南区」',
|
||||
`level` TINYINT NOT NULL COMMENT '1=省级 2=地级市 3=区县(含市直管镇)',
|
||||
`parent_code` VARCHAR(12) DEFAULT NULL COMMENT '上级代码;省级为 NULL',
|
||||
PRIMARY KEY (`code`),
|
||||
KEY `idx_region_parent` (`parent_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='全国行政区划(省/市/区县三级):地址结构化与导入时对齐用'
|
||||
SQL,
|
||||
];
|
||||
}
|
||||
@@ -807,6 +820,36 @@ SQL,
|
||||
// 就要为页面上的每个人回读一次存储,几十个人 = 几十次外网往返。
|
||||
self::addColumn($db, 'persons', 'avatar_color',
|
||||
"ADD COLUMN `avatar_color` CHAR(7) DEFAULT NULL COMMENT '头像主色(上传时算一次;页面按它派生底色/字色)' AFTER `avatar`");
|
||||
|
||||
// ---- 地址结构化(版本 16)----
|
||||
// 用户 2026-10-04 第 4 条:地址 = 国家 / 省 / 市 / 区 / 详细地址五级,
|
||||
// 而且「如果没有非常详细的地址,就只记到区」—— 所以四级都可空,
|
||||
// 只有 `address`(详细地址)是原来那一个自由文本列,语义不变。
|
||||
// ⚠️ 存的是**标准名**不是代码:通讯录导进来的地址是自由文本,
|
||||
// 按代码存就要求导入时 100% 匹配得上,匹配不上的整条地址就丢了;
|
||||
// 存标准名则"对得上就规范化、对不上就照抄",永远不会更差。
|
||||
self::addColumn($db, 'persons', 'addr_country',
|
||||
"ADD COLUMN `addr_country` VARCHAR(60) DEFAULT NULL COMMENT '国家' AFTER `address`");
|
||||
self::addColumn($db, 'persons', 'addr_province',
|
||||
"ADD COLUMN `addr_province` VARCHAR(60) DEFAULT NULL COMMENT '省 / 自治区 / 直辖市 / 特别行政区(标准名,对应 regions.level=1)' AFTER `addr_country`");
|
||||
self::addColumn($db, 'persons', 'addr_city',
|
||||
"ADD COLUMN `addr_city` VARCHAR(60) DEFAULT NULL COMMENT '地级市(标准名,对应 regions.level=2)' AFTER `addr_province`");
|
||||
self::addColumn($db, 'persons', 'addr_district',
|
||||
"ADD COLUMN `addr_district` VARCHAR(60) DEFAULT NULL COMMENT '区 / 县(标准名,对应 regions.level=3)' AFTER `addr_city`");
|
||||
// ⚠️ CREATE TABLE IF NOT EXISTS 不会改已存在的列宽 —— 老库(v16 之前建的
|
||||
// `code VARCHAR(6)`)必须显式 MODIFY,否则东莞那种 9 位镇级代码插不进去。
|
||||
$db->exec("ALTER TABLE `regions` MODIFY COLUMN `code`
|
||||
VARCHAR(12) NOT NULL COMMENT '行政区划代码:省 2 位 / 市 4 位 / 区县 6 位(市直管镇的镇 9 位)'");
|
||||
$db->exec("ALTER TABLE `regions` MODIFY COLUMN `parent_code`
|
||||
VARCHAR(12) DEFAULT NULL COMMENT '上级代码;省级为 NULL'");
|
||||
// 全国行政区划灌库(只是数据步骤 → 容错跑:种子文件丢了也不该挡住后面的升级)
|
||||
self::tryStep('灌入全国行政区划(省/市/区县)', static function () use ($db) {
|
||||
\App\Region::seed($db);
|
||||
});
|
||||
// 老地址(原来那一整串)拆成结构化:只补空字段,绝不覆盖用户已经改过的
|
||||
self::tryStep('老地址拆成国家/省/市/区', static function () use ($db) {
|
||||
self::backfillAddress($db);
|
||||
});
|
||||
// 老头像(v11 之前传的)补算一次:回读已存的图算主色。
|
||||
// 纯数据步骤 → 容错跑:某张图读不回来(OSS 权限、对象丢了)只影响那一个人,
|
||||
// 他会自动落到「按姓名取色」的兜底上,不该把后面的升级步骤挡住。
|
||||
@@ -1386,6 +1429,41 @@ SQL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 老地址(原来 `address` 里那一整串自由文本)拆成 国家/省/市/区。
|
||||
*
|
||||
* ⚠️ 只补**空着**的四个字段,绝不覆盖:用户如果已经手工选过省市区,
|
||||
* 一次升级把他选的改成"从文本里猜的",比不猜更糟(猜错就悄悄错了)。
|
||||
* ⚠️ 拆不出来的(比如只写了「老家」)就**原样留在 address 里** ——
|
||||
* 详细地址那一栏本来就允许是任意文本,不拆等于没变,不会丢东西。
|
||||
*/
|
||||
private static function backfillAddress(\PDO $db): void
|
||||
{
|
||||
$rows = $db->query(
|
||||
"SELECT id, address FROM persons
|
||||
WHERE address IS NOT NULL AND address <> ''
|
||||
AND addr_province IS NULL AND addr_city IS NULL AND addr_district IS NULL
|
||||
ORDER BY id"
|
||||
)->fetchAll();
|
||||
if (!$rows) {
|
||||
return;
|
||||
}
|
||||
$up = $db->prepare(
|
||||
'UPDATE persons SET addr_country = ?, addr_province = ?, addr_city = ?,
|
||||
addr_district = ?, address = ? WHERE id = ?'
|
||||
);
|
||||
foreach ($rows as $r) {
|
||||
$p = \App\Region::parse((string)$r['address']);
|
||||
if ($p['province'] === null && $p['city'] === null && $p['district'] === null) {
|
||||
continue; // 什么都认不出来 → 整串留在详细地址里,不动它
|
||||
}
|
||||
$up->execute([
|
||||
$p['country'], $p['province'], $p['city'],
|
||||
$p['district'], $p['detail'] ?? '', (int)$r['id'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 列不存在才加 */
|
||||
private static function addColumn(\PDO $db, string $table, string $column, string $alter): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user