# 技术方案与详细设计 > 说明:本文件保留了项目早期方案与演进背景。若其中出现旧 `/entry`、两级“关系分类 -> 具体关系”、默认标签池或旧主题入口描述,请以当前代码基线为准:搜索字段统一为户主、成员、拼音、标签、备注;系统初始化后不预置默认标签;关系分类支持按需新增;快速录入与分享页分别服务婚礼当天录礼金和链接内简化编辑。 ## 1. 技术栈 ## 1.1 后端 - Python 3 - 建议框架:Flask - 模板引擎:Jinja2 - ORM:SQLAlchemy 选择原因: - 适合中小型 CRUD 系统 - 服务端渲染配合 jQuery 实现简单、直接 - 易于快速开发账号、表单、列表、导出等功能 ## 1.2 前端 - jQuery - Tailwind CSS(CLI 编译模式) - 少量自定义 CSS 当前实现基线: - 页面继续采用 Flask + Jinja2 SSR 输出完整 HTML - 页面交互增强以原生 JavaScript 为主,保留与 jQuery 技术栈兼容的轻量增强模式 - 管理首页与长辈页通过 SSR + partial HTML + fetch 实现动态刷新,仍保留普通 GET / POST fallback - 中文 / 拼音 / 首字母搜索在服务端 Python 中完成,依赖 `pypinyin` - CSV 导入导出在服务端实现,依赖 Python 标准库 `csv` - 端到端测试采用 Python + Playwright + pytest-playwright ### 主题系统实现 - 支持 light / dark 双主题切换,切换入口位于右上角用户菜单 - 使用 `` 驱动暗色主题样式切换 - 使用 `localStorage('hw-theme')` 持久化用户偏好,并结合 `prefers-color-scheme` 作为默认值 - 在页面最前面执行同步主题初始化脚本,避免首屏 FOUC - 同步设置 `document.documentElement.style.colorScheme`,让浏览器原生控件跟随主题 ### 样式构建方案 - 当前采用 **Tailwind CSS v4 CLI** 进行样式编译,不再依赖浏览器运行时 CDN 解析 - 源文件:`app/static/src/styles.css` - 输出文件:`app/static/css/main.css` - 模板入口:`app/templates/base.html` 通过 Flask `url_for('static', filename='css/main.css')` 引用 - `styles.css` 中集中维护: - `@theme` 品牌色与字体变量 - `@layer base`(如 `.sr-only`) - `@layer components`(如 `.btn`、`.card`、`.status-badge`、侧边栏壳层) - 通过 `@source "../../templates"` 扫描 Jinja 模板中的 Tailwind 类名,保证编译产物覆盖 SSR 页面所需样式 - 本地开发时通过 `npm run build:css` / `npm run watch:css` 生成编译产物 ## 1.3 数据库 - 正式环境:MySQL - 本地测试:SQLite 实现原则: - 尽量使用 ORM - 尽量避免写死数据库特性差异较大的 SQL - 金额使用 Decimal 语义处理,避免浮点误差 ## 1.4 页面模式 - 以前后端一体为主 - 页面由 Flask + Jinja2 服务端渲染 - 已登录后台页面采用 sidebar-first 布局,未登录登录页保持轻量居中布局 - jQuery 用于: - 搜索 - 筛选 - 局部刷新 - 弹窗编辑 - 导入导出交互 ## 2. 目录结构建议 建议后续项目结构: ```text hw/ ├── README.md ├── docs/ │ └── technical-design.md ├── app/ │ ├── __init__.py │ ├── config.py │ ├── models/ │ ├── services/ │ ├── routes/ │ ├── templates/ │ └── static/ ├── migrations/ ├── tests/ └── run.py ``` ## 3. 核心模块设计 ## 3.1 认证与账号模块 ### 目标 提供简单账号密码登录,满足以下要求: - 基本身份识别 - 长辈专用账号独立追踪 - 审计日志可关联到具体操作人 - 管理员可自行创建新账号 ### 账号角色建议 - `admin`:完整权限 - `editor`:可查看、录入、编辑业务数据 - `entry_only`:仅可访问快速录入页,并且只能修改当天高频字段 ### 登录机制建议 - 使用账号名 + 密码登录 - 密码必须加密存储 - 使用服务器 session 维持登录态 - 提供退出登录 - 提供账号启停能力 - 首个管理员账号在系统初始化阶段手动创建 - 后续账号由管理员在后台直接创建 ### 密码与会话建议 - 密码哈希建议优先使用 `werkzeug.security` 或等价安全方案 - 不保存明文密码 - 可提供管理员重置密码能力 - Session 登录态适合当前服务端渲染项目,复杂度低于 JWT - 登录成功、失败、退出登录都建议记审计日志 ### 页面与权限建议 | 页面 | admin | editor | entry_only | |---|---:|---:|---:| | 管理首页 | ✅ | ✅ | ❌ | | 快速录入页 | ✅ | ✅ | ✅ | | 分享搜索 / 分享编辑页 | 公开链接 | 公开链接 | 公开链接 | | CSV 导入导出 | ✅ | ✅ | ❌ | | 账号管理 | ✅ | ❌ | ❌ | | 审计日志 | ✅ | 可选 | ❌ | ### 快速录入账号设计 建议为长辈创建单独账号,例如: - `father-side-a` - `mother-side-b` - `uncle-entry` 登录后进入固定的快速录入页,不让其接触复杂管理页。 好处: - 使用路径简单 - 可以知道是谁完成了搜索后的修改 - 审计日志天然具备来源追踪 ### 快速录入页访问流建议 建议访问流如下: 1. 管理员在后台创建长辈专用账号 2. 为该账号分配 `entry_only` 角色 3. 将专用登录入口提供给对应长辈 4. 长辈使用账号密码登录 5. 登录成功后系统自动跳转到 `/quick-entry` 6. 录入人员先搜索系统中已存在的户 7. 选择目标户后进入单独编辑页;若搜不到,可直接新增一户并记礼金 8. 所有修改行为自动写入 `updated_by` 9. 审计日志记录本次登录与保存修改行为 说明: - “专属入口”建议理解为“专用登录入口 + 专属账号身份”;公开分享页另走分享链接模式 - 本次婚宴涉及的户应由管理员预先录入系统,长辈账号不负责纯新增 - 这样既简单,也便于后续审计和权限控制 ## 3.2 数据库建模约定 ### 3.2.1 MVP 表范围 当前阶段保持 **6 张核心表**,避免过早拆成过多小表: 1. `accounts` 2. `households` 3. `household_members` 4. `gift_records` 5. `audit_logs` 6. `option_items` 说明: - `households` 是核心主表,承接户级主信息、邀请状态、到场状态、回礼状态与聚合金额 - `gift_records` 负责礼金 / 礼品明细,不再额外引入独立的“回礼记录表”作为第 7 张表 - 喜糖、伴手礼、儿童红包等回礼发放状态在 MVP 中直接落到 `households`,便于长辈页和管理页快速编辑 - 若后续需要追踪每次回礼发放明细,再新增独立明细表,不在当前 MVP 范围内 ### 3.2.2 通用字段与类型约定 - 主键统一使用 `BIGINT`(SQLite 本地测试可映射为 `INTEGER` 自增主键) - 时间字段统一使用 `DATETIME` - 金额字段统一使用 `DECIMAL(12,2)` - 布尔语义字段在 MySQL 中使用 `TINYINT(1)`,在 ORM 层映射为布尔值 - JSON 结构字段在 MySQL 中优先使用 `JSON`,SQLite 本地测试阶段可用 `TEXT` 存 JSON 字符串 - 文本备注字段默认使用 `TEXT` - 软删除统一使用 `deleted_at DATETIME NULL` - 并发控制统一使用 `version INT NOT NULL DEFAULT 1` ### 3.2.3 软删除与外键约定 当前阶段的软删除策略: - `households`:**软删除**,避免删除后丢失礼金、人情往来、审计上下文 - `gift_records`:**软删除**,避免误删礼金明细后不可追溯 - `accounts`:默认**不物理删除**,建议通过 `status=disabled` 停用;MVP 可不加 `deleted_at` - `household_members`:MVP 中可直接物理删除,但删除动作要记审计日志;若后续需要支持恢复误删成员,可再补 `deleted_at` - `option_items`:默认不物理删除,通过 `is_enabled` 停启、`is_system` 保护系统项 - `audit_logs`:默认只追加、不删除 外键约定: - `household_members.household_id -> households.id` - `gift_records.household_id -> households.id` - `households.created_by / updated_by -> accounts.id` - `household_members.created_by / updated_by -> accounts.id` - `gift_records.created_by / updated_by -> accounts.id` - `option_items.created_by / updated_by -> accounts.id` - `audit_logs.actor_user_id -> accounts.id`(可空,保留用户名快照) - `households.relation_category_option_id -> option_items.id` - `households.gift_method_option_id -> option_items.id` - `households.gift_scene_option_id -> option_items.id` - `gift_records.method_option_id -> option_items.id` - `gift_records.scene_option_id -> option_items.id` - `option_items.parent_id -> option_items.id` 推荐外键行为: - 对 `households` 的子表(如 `household_members`、`gift_records`)使用 `ON DELETE RESTRICT`,避免误删主表时破坏历史数据 - 对 `created_by` / `updated_by` / `actor_user_id` 建议使用 `ON DELETE SET NULL`,配合用户名快照保留审计信息 - 对 `option_items` 关联建议使用 `ON DELETE RESTRICT`,防止配置项被删除后造成业务数据悬空 ### 3.2.4 枚举与动态选项约定 为兼顾“够用就行”和“后续可配置”,当前采用两类策略: 1. **固定枚举,代码常量维护** - `accounts.role`: `admin` / `editor` / `entry_only` - `accounts.status`: `active` / `disabled` - `households.side`: `groom_side` / `bride_side` / `both_side` - `households.invite_status`: `not_invited` / `invited` / `confirmed` / `declined` - `households.attendance_status`: `pending` / `attending` / `absent` / `partial` - `households.favor_status`: `not_given` / `given` - `households.candy_status`: `not_given` / `given` - `households.child_red_packet_status`: `not_given` / `partial` / `given` - `gift_records.record_type`: `cash_gift` / `physical_gift` / `pre_wedding_cash` / `wedding_day_cash` / `post_wedding_cash` 2. **可配置选项,使用 `option_items` 维护** - 关系分类 - 标签 - 礼金方式 - 礼金记录场景 - 后续如有需要,也可扩展更多下拉选项组 说明: - `relation_category`、标签等不写死,满足后续新增 / 启停 / 排序需求 - `side` 仍保留为固定枚举,因为范围稳定且影响筛选频率高;展示层应统一映射为中文友好文案,避免直接暴露原始枚举值 - 标签在 MVP 中使用 `tag_option_ids_json` 保存选中的 `option_items.id` 数组,避免为了标签单独增加第 7 张关联表 - 这意味着标签引用完整性主要由应用层保证;若未来 SQL 级标签统计需求增强,再补充 `household_tag_links` 一类的关联表 ## 3.3 账号与审计模块 ### 3.3.1 `accounts` 表 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `username` | VARCHAR(64) | 是 | - | 登录账号,唯一 | | `password_hash` | VARCHAR(255) | 是 | - | 密码哈希,不存明文 | | `display_name` | VARCHAR(64) | 否 | NULL | 显示名称,如“爸爸录入账号” | | `role` | VARCHAR(32) | 是 | `editor` | 角色:`admin` / `editor` / `entry_only` | | `status` | VARCHAR(16) | 是 | `active` | 状态:`active` / `disabled` | | `last_login_at` | DATETIME | 否 | NULL | 最后登录时间 | | `created_by` | BIGINT | 否 | NULL | 创建人账号 ID | | `updated_by` | BIGINT | 否 | NULL | 最后更新人账号 ID | | `created_at` | DATETIME | 是 | 当前时间 | 创建时间 | | `updated_at` | DATETIME | 是 | 当前时间 | 更新时间 | 约束与索引: - `UNIQUE KEY uk_accounts_username (username)` - `KEY idx_accounts_role_status (role, status)` - `KEY idx_accounts_last_login_at (last_login_at)` 实现说明: - 新账号默认 `status=active` - 不建议物理删除账号,优先停用 - 长辈专用账号通过 `role=entry_only` 实现能力边界 ### 3.3.2 `audit_logs` 表 ### 设计目标 系统中的关键变更必须可追溯。 ### 记录范围 至少记录: - 登录 - 登出 - 新增一户 - 编辑一户 - 删除 / 软删除一户 - 新增 / 删除成员 - 长辈页保存修改 - 导入 CSV - 创建账号 - 重置密码 - 启停账号 ### 字段定义 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `actor_user_id` | BIGINT | 否 | NULL | 操作人账号 ID,允许为空 | | `actor_username` | VARCHAR(64) | 是 | - | 操作时账号名快照 | | `actor_display_name` | VARCHAR(64) | 否 | NULL | 操作时显示名快照 | | `action_type` | VARCHAR(32) | 是 | - | 动作类型,如 `login`、`update_household` | | `target_type` | VARCHAR(32) | 是 | - | 目标类型,如 `household`、`account` | | `target_id` | BIGINT | 否 | NULL | 目标对象 ID | | `target_display_name` | VARCHAR(128) | 否 | NULL | 目标对象名称快照 | | `before_data_json` | JSON / TEXT | 否 | NULL | 变更前摘要 | | `after_data_json` | JSON / TEXT | 否 | NULL | 变更后摘要 | | `request_path` | VARCHAR(255) | 否 | NULL | 请求路径 | | `request_method` | VARCHAR(16) | 否 | NULL | 请求方法 | | `ip_address` | VARCHAR(45) | 否 | NULL | IPv4 / IPv6 | | `user_agent` | VARCHAR(512) | 否 | NULL | 浏览器标识 | | `created_at` | DATETIME | 是 | 当前时间 | 记录时间 | 推荐索引: - `KEY idx_audit_logs_created_at (created_at)` - `KEY idx_audit_logs_actor_created_at (actor_user_id, created_at)` - `KEY idx_audit_logs_action_created_at (action_type, created_at)` - `KEY idx_audit_logs_target (target_type, target_id, created_at)` 实现说明: - 审计日志默认只追加,不做软删除 - 即使账号后续改名或停用,仍可通过 `actor_username` / `actor_display_name` 快照追溯历史 - 不建议记录普通搜索行为,避免日志噪音过大 ### 审计展示建议 日志页需支持: - 时间倒序 - 按人筛选 - 按动作筛选 - 按对象筛选 - 查看变更前后对比摘要 ## 3.4 户、成员与礼金模块 ### 3.4.1 `households` 表 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `household_code` | VARCHAR(32) | 是 | - | 户唯一编码,便于导入导出和人工核对 | | `head_name` | VARCHAR(64) | 是 | - | 户主姓名 | | `phone` | VARCHAR(32) | 否 | NULL | 联系电话 | | `side` | VARCHAR(32) | 是 | `groom_side` | 所属侧 | | `relation_category_option_id` | BIGINT | 否 | NULL | 关系分类选项 | | `tag_option_ids_json` | JSON / TEXT | 否 | NULL | 选中的标签 ID 数组 | | `invite_status` | VARCHAR(32) | 是 | `not_invited` | 邀请状态 | | `attendance_status` | VARCHAR(32) | 是 | `pending` | 到场状态 | | `expected_attendee_count` | INT | 是 | `0` | 预计到场人数 | | `actual_attendee_count` | INT | 是 | `0` | 实际到场人数 | | `child_count` | INT | 是 | `0` | 儿童人数 | | `red_packet_child_count` | INT | 是 | `0` | 需红包儿童人数 | | `total_gift_amount` | DECIMAL(12,2) | 是 | `0.00` | 汇总礼金金额 | | `gift_method_option_id` | BIGINT | 否 | NULL | 当前主礼金方式 | | `gift_scene_option_id` | BIGINT | 否 | NULL | 当前主礼金记录场景 | | `favor_status` | VARCHAR(32) | 是 | `not_given` | 伴手礼状态 | | `candy_status` | VARCHAR(32) | 是 | `not_given` | 喜糖状态 | | `child_red_packet_status` | VARCHAR(32) | 是 | `not_given` | 儿童红包状态 | | `note` | TEXT | 否 | NULL | 备注 | | `created_by` | BIGINT | 否 | NULL | 创建人账号 ID | | `updated_by` | BIGINT | 否 | NULL | 最后更新人账号 ID | | `created_at` | DATETIME | 是 | 当前时间 | 创建时间 | | `updated_at` | DATETIME | 是 | 当前时间 | 更新时间 | | `deleted_at` | DATETIME | 否 | NULL | 软删除时间 | | `version` | INT | 是 | `1` | 乐观锁版本号 | 约束与索引: - `UNIQUE KEY uk_households_household_code (household_code)` - `KEY idx_households_head_name (head_name)` - `KEY idx_households_phone (phone)` - `KEY idx_households_side_relation_category (side, relation_category_option_id)` - `KEY idx_households_attendance_status (attendance_status)` - `KEY idx_households_updated_at (updated_at)` - `KEY idx_households_deleted_at (deleted_at)` 推荐应用层校验: - `actual_attendee_count >= 0` - `expected_attendee_count >= 0` - `child_count >= 0` - `red_packet_child_count >= 0` - `red_packet_child_count <= child_count` - `actual_attendee_count` 不应明显大于成员数与预计人数总和,若超出则给出提示 - `phone` 允许为空,但若填写应做基础格式校验 实现说明: - `entry_only` 角色默认不能修改户的核心身份字段,如户主姓名、所属侧、关系分类、标签 - `entry_only` 角色仅允许修改到场、礼金、备注等当天高频字段 - `gift_method_option_id` 与 `gift_scene_option_id` 用于户级“当前主状态”,详细礼金流水仍写入 `gift_records` - `total_gift_amount` 属于聚合缓存字段,新增 / 修改 / 软删除 / 恢复 `gift_records` 后都应重新汇总该户的有效礼金金额,避免长期与明细脱节 - `version` 用于乐观锁,保存时校验前端版本号与数据库版本号一致,再执行 `version = version + 1` ### 3.4.2 `household_members` 表 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `household_id` | BIGINT | 是 | - | 所属户 ID | | `name` | VARCHAR(64) | 是 | - | 成员姓名 | | `relation_to_head` | VARCHAR(64) | 否 | NULL | 与户主关系 | | `gender` | VARCHAR(16) | 否 | NULL | 可选,MVP 可仅做展示辅助 | | `age_group` | VARCHAR(32) | 否 | NULL | 可选,如儿童 / 成人 / 长辈 | | `is_child` | TINYINT(1) | 是 | `0` | 是否儿童 | | `needs_red_packet` | TINYINT(1) | 是 | `0` | 是否需要儿童红包 | | `expected_to_attend` | TINYINT(1) | 是 | `1` | 是否预计到场 | | `actually_attended` | TINYINT(1) | 是 | `0` | 是否实际到场 | | `sort_order` | INT | 是 | `0` | 展示顺序 | | `note` | TEXT | 否 | NULL | 备注 | | `created_by` | BIGINT | 否 | NULL | 创建人账号 ID | | `updated_by` | BIGINT | 否 | NULL | 最后更新人账号 ID | | `created_at` | DATETIME | 是 | 当前时间 | 创建时间 | | `updated_at` | DATETIME | 是 | 当前时间 | 更新时间 | 约束与索引: - `KEY idx_household_members_household_id (household_id)` - `KEY idx_household_members_household_sort (household_id, sort_order)` - `KEY idx_household_members_household_name (household_id, name)` 实现说明: - 成员表当前不做软删除,误删时依靠审计日志回溯 - `gender`、`age_group` 来自需求文档中的可选字段,MVP 可以先保留字段,不做复杂校验 - 成员数据主要服务于人数核对、儿童红包判断和后续桌席安排 ### 3.4.3 `gift_records` 表 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `household_id` | BIGINT | 是 | - | 所属户 ID | | `record_type` | VARCHAR(32) | 是 | `cash_gift` | 记录类型 | | `amount` | DECIMAL(12,2) | 否 | NULL | 现金礼金金额 | | `gift_name` | VARCHAR(128) | 否 | NULL | 礼品名称 | | `estimated_value` | DECIMAL(12,2) | 否 | NULL | 礼品估值 | | `method_option_id` | BIGINT | 否 | NULL | 礼金方式 | | `scene_option_id` | BIGINT | 否 | NULL | 记录场景 | | `record_time` | DATETIME | 是 | 当前时间 | 记录时间 | | `created_by` | BIGINT | 否 | NULL | 创建人账号 ID | | `updated_by` | BIGINT | 否 | NULL | 最后更新人账号 ID | | `deleted_at` | DATETIME | 否 | NULL | 软删除时间 | | `note` | TEXT | 否 | NULL | 备注 | | `created_at` | DATETIME | 是 | 当前时间 | 创建时间 | | `updated_at` | DATETIME | 是 | 当前时间 | 更新时间 | 约束与索引: - `KEY idx_gift_records_household_id (household_id)` - `KEY idx_gift_records_record_time (record_time)` - `KEY idx_gift_records_household_record_time (household_id, record_time)` - `KEY idx_gift_records_record_type (record_type)` - `KEY idx_gift_records_deleted_at (deleted_at)` 推荐应用层校验: - `amount` 与 `estimated_value` 至少要有一个非空 - 纯现金记录通常要求 `amount` 非空 - 纯礼品记录通常要求 `gift_name` 非空 - 删除礼金记录后,需要同步重算 `households.total_gift_amount` 实现说明: - `gift_records` 是明细表,`households.total_gift_amount` 是聚合展示字段 - 长辈页可只维护户级聚合状态;管理页可进一步查看或补录详细礼金明细 ## 3.5 动态选项模块 ### 3.5.1 `option_items` 表 用于维护可配置下拉项和标签池,覆盖关系分类、标签、礼金方式、记录场景等。 | 字段 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | `id` | BIGINT | 是 | 自增 | 主键 | | `option_group` | VARCHAR(32) | 是 | - | 选项分组,如 `relation_category` | | `option_code` | VARCHAR(64) | 是 | - | 组内唯一编码 | | `option_label` | VARCHAR(64) | 是 | - | 显示文案 | | `parent_id` | BIGINT | 否 | NULL | 父选项 ID,当前主要为兼容历史结构保留 | | `sort_order` | INT | 是 | `0` | 排序值 | | `is_enabled` | TINYINT(1) | 是 | `1` | 是否启用 | | `is_system` | TINYINT(1) | 是 | `0` | 是否系统保护项 | | `extra_json` | JSON / TEXT | 否 | NULL | 扩展元数据 | | `created_by` | BIGINT | 否 | NULL | 创建人账号 ID | | `updated_by` | BIGINT | 否 | NULL | 最后更新人账号 ID | | `created_at` | DATETIME | 是 | 当前时间 | 创建时间 | | `updated_at` | DATETIME | 是 | 当前时间 | 更新时间 | 约束与索引: - `UNIQUE KEY uk_option_items_group_code (option_group, option_code)` - `UNIQUE KEY uk_option_items_group_parent_label (option_group, parent_id, option_label)` - `KEY idx_option_items_group_enabled_sort (option_group, is_enabled, sort_order)` - `KEY idx_option_items_parent_id (parent_id)` 实现说明: - 标签项使用 `option_group = tag` - 礼金方式使用 `option_group = gift_method` - 礼金记录场景使用 `option_group = gift_scene` - 若未来恢复多级选项,可继续利用 `parent_id` ### 3.5.2 推荐分组清单 - `relation_category` - `tag` - `gift_method` - `gift_scene` ## 3.6 数据库索引与校验建议 ### 3.6.1 高频查询对应的索引目标 1. **长辈页搜索既有户** - 主要字段:`head_name`、`phone` - 对应索引:`idx_households_head_name`、`idx_households_phone` 2. **管理页按所属侧 / 关系分类 / 状态筛选** - 主要字段:`side`、`relation_category_option_id`、`attendance_status` - 对应索引:`idx_households_side_relation_category`、`idx_households_attendance_status` 3. **查看某户礼金历史** - 主要字段:`household_id`、`record_time` - 对应索引:`idx_gift_records_household_record_time` 4. **管理首页组合筛选与最近更新时间排序** - 主要字段:`side`、`invite_status`、`attendance_status`、`updated_at` - 对应索引:`idx_households_side_relation_category`、`idx_households_attendance_status`、`idx_households_updated_at` 5. **账号登录与权限过滤** - 主要字段:`username`、`role`、`status` - 对应索引:`uk_accounts_username`、`idx_accounts_role_status` 6. **审计日志筛选** - 主要字段:`created_at`、`actor_user_id`、`action_type`、`target_type + target_id` - 对应索引:审计日志相关索引组合 ### 3.6.2 应用层校验建议 - `household_code` 应由系统统一生成,避免手输重复 - 导入 CSV 时先按 `household_code` 判断强匹配,再结合 `head_name + phone` 做疑似重复提示 - 写入 `tag_option_ids_json` 时,应校验每个 ID 对应的 `option_group=tag` - 对标签相关查询尽量走应用层过滤,避免在 MVP 阶段强依赖 MySQL / SQLite 不一致的 JSON 查询语法 - 写入 `gift_method_option_id` 时,应校验其分组为 `gift_method` - 写入 `gift_scene_option_id` / `scene_option_id` 时,应校验其分组为 `gift_scene` - 所有被停用的 `option_items` 默认不再用于新建和编辑,但旧数据仍可正常展示 - 更新户级礼金金额时,应优先以 `gift_records` 重算,避免长期依赖手工汇总 ## 4. 搜索方案设计 ## 4.1 目标 支持: - 中文搜索 - 拼音全拼搜索 - 首字母搜索 - 混合搜索 - 多字段统一搜索 ## 4.2 当前实现方案 当前搜索能力已经落地在服务端 `app/services/households.py` 中: - 中文原文匹配 - `pypinyin` 全拼匹配 - `pypinyin` 首字母匹配 - 与户主姓名、成员姓名、标签、备注等字段的统一搜索 这样做的原因: - SSR 页面刷新与导出 CSV 复用同一套筛选语义,更容易保持一致 - 避免浏览器端额外维护一份搜索索引和拼音转换逻辑 - 当前数据规模适合在服务端完成筛选与排序 - 测试可以直接覆盖服务层行为,验证成本更低 ## 4.3 搜索字段与后续增强建议 当前服务端搜索匹配重点覆盖: - 户主姓名 - 手机号 - 户编码 - 备注 - 户主姓名对应的拼音全拼 - 户主姓名对应的拼音首字母 当前首页已经同时支持: - `side / invite_status / attendance_status` 组合筛选 - `updated_at / head_name / expected_attendee_count / actual_attendee_count / child_count / total_gift_amount` 排序 - 筛选后的 count 与统计摘要 - 管理首页 `partial=results`、快速录入页 `partial=search-results`、分享搜索页 `partial=results` 的 partial 响应分支 - 前端 300ms debounce + `fetch` + DOM 局部替换 + `history.replaceState` 后续如果数据量明显增大,可再考虑: - 在后端持久化拼音字段 - 预生成搜索索引字段 - 将统计从内存汇总进一步下推到数据库查询 ## 5. 表格与首页交互设计 ## 5.1 表格方案建议 当前实现不依赖额外表格库,而是直接使用 SSR 表格模板: - 管理首页使用服务端渲染的原生 `