You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

38 KiB

技术方案与详细设计

说明:本文件保留了项目早期方案与演进背景。若其中出现旧 /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 双主题切换,切换入口位于右上角用户菜单
  • 使用 <html class="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. 目录结构建议

建议后续项目结构:

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_membersgift_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) - 动作类型,如 loginupdate_household
target_type VARCHAR(32) - 目标类型,如 householdaccount
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_idgift_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)

实现说明:

  • 成员表当前不做软删除,误删时依靠审计日志回溯
  • genderage_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)

推荐应用层校验:

  • amountestimated_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_namephone
    • 对应索引:idx_households_head_nameidx_households_phone
  2. 管理页按所属侧 / 关系分类 / 状态筛选

    • 主要字段:siderelation_category_option_idattendance_status
    • 对应索引:idx_households_side_relation_categoryidx_households_attendance_status
  3. 查看某户礼金历史

    • 主要字段:household_idrecord_time
    • 对应索引:idx_gift_records_household_record_time
  4. 管理首页组合筛选与最近更新时间排序

    • 主要字段:sideinvite_statusattendance_statusupdated_at
    • 对应索引:idx_households_side_relation_categoryidx_households_attendance_statusidx_households_updated_at
  5. 账号登录与权限过滤

    • 主要字段:usernamerolestatus
    • 对应索引:uk_accounts_usernameidx_accounts_role_status
  6. 审计日志筛选

    • 主要字段:created_atactor_user_idaction_typetarget_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 表格模板:

  • 管理首页使用服务端渲染的原生 <table>
  • 默认只读,单条编辑跳转到独立编辑页
  • 通过 query params 维持搜索、筛选、排序与导出上下文

这样做的原因:

  • 技术复杂度更低,适合当前核心可用版本
  • 与 Tailwind 样式集成简单
  • 对长辈和非技术用户更直观,避免前端重交互表格带来的认知负担

如果后续确实出现更复杂的分页、列操作、批量编辑需求,再评估是否引入更重的表格库。

5.2 首页布局与动态刷新

当前实现遵循“高频操作留在主区,低频操作收纳到右上角”的原则:

左侧边栏
├── 管理首页
├── 快速录入
├── (admin)账号管理 / 审计日志
└── (admin)分享链接

首页头部
├── 页面标题
└── 右上角账户菜单
    ├── 主题切换
    └── 退出登录

统计总览区
├── 总户数
├── 总预计人数
├── 儿童数
└── 礼金总额

搜索与筛选区
├── 快速搜索框
├── 所属侧 / 邀请状态 / 到场状态筛选
├── 排序字段 / 排序方向
└── 搜索 / 重置

结果区(partial)
├── 当前结果与筛选后摘要
├── 快速概览卡片
├── 桌面表格
└── 移动卡片列表

动态刷新策略:

  • 管理首页保留完整 GET / SSR 页面
  • 新增 partial=results / X-HW-Partial: results 分支,仅返回 main/_household_results.html
  • 前端使用 300ms debounce 避免高频输入触发过多请求
  • 响应成功后只替换 #household-results-region
  • 同步调用 history.replaceState 保持 URL 与当前筛选条件一致
  • 请求失败时仅记录前端错误日志,不破坏已有页面内容

5.3 编辑交互建议

流程:

  1. 查看列表
  2. 点击“修改”
  3. 打开单独编辑页
  4. 修改字段
  5. 点击“保存并返回搜索”或“保存”
  6. 提交成功
  7. 写入审计日志

不建议

  • 默认行内直接可编辑
  • 打开页面就是输入框
  • 修改后自动保存

6. 快速录入页设计

6.1 页面目标

  • 极简
  • 单列
  • 搜索后快速进入当天录礼金
  • 支持“搜不到时快速新增一户并记礼金”
  • 不承担完整资料维护职责
  • 尽量降低改错人的风险

6.2 页面结构建议

当前实现采用“搜索页 -> 单独编辑页 / 新增页”的模式:

  1. 搜索目标户
  2. 搜索结果卡片列表实时刷新
  3. 点击“编辑”后进入单独编辑页
  4. 编辑页只保留当天高频字段
  5. 保存后返回搜索页

6.3 搜索页建议

搜索字段建议支持:

  • 户主姓名
  • 成员姓名
  • 拼音 / 首字母
  • 标签
  • 备注

结果列表建议展示:

  • 明确的“编辑”按钮
  • 户主姓名
  • 所属侧
  • 关系分类
  • 当前状态摘要

6.4 允许修改字段建议

  • 到场状态
  • 实际到场人数
  • 儿童人数
  • 需红包儿童人数
  • 礼金金额
  • 礼金方式
  • 礼金记录场景
  • 备注

6.5 只读展示字段建议

  • 户主姓名
  • 所属侧
  • 关系分类
  • 预计人数

6.6 交互建议

  • 登录后默认进入该页
  • 先搜索再选择目标户
  • 搜索输入时使用 300ms debounce 实时刷新结果列表
  • 编辑表单使用单独页面承载
  • 保存成功显示明显成功提示
  • 数量类字段尽量在前端做即时校验,并保留服务端兜底校验,避免用户误填后直到提交才发现问题
  • 搜索页应同时提供“新增”入口,便于当天现场处理未预录来宾

6.7 ASCII 草图建议

搜索页

+--------------------------------------------------------------+
| 完善来宾信息                                                 |
| 当前账号:爸爸专用账号                                           |
+--------------------------------------------------------------+
| 请输入户主姓名或手机号                                       |
| [______________________________] [搜索]                      |
| 提示:只可修改已经录入系统的来宾信息                         |
+--------------------------------------------------------------+
| 搜索结果                                                     |
+--------------------------------------------------------------+
| 王大爷一家                                                   |
| 电话:138****8888                                            |
| 所属侧:男方                                                 |
| 关系:亲戚 / 表叔                                            |
| 当前状态:待确认,礼金未登记                                 |
| [选择此户]                                                   |
+--------------------------------------------------------------+

确认页

+--------------------------------------------------------------+
| 请确认您要编辑的是以下来宾                                   |
+--------------------------------------------------------------+
| 户主:王大爷                                                 |
| 电话:138****8888                                            |
| 所属侧:男方                                                 |
| 关系:亲戚 / 表叔                                            |
| 预计人数:3                                                  |
| 当前状态:待确认                                             |
| [返回搜索]                    [确认编辑这户]                 |
+--------------------------------------------------------------+

简化编辑页

+--------------------------------------------------------------+
| 编辑:王大爷一家                                             |
+--------------------------------------------------------------+
| 以下信息仅供核对,不可修改                                   |
| 户主姓名:王大爷                                             |
| 所属侧:男方                                                 |
| 关系:亲戚 / 表叔                                            |
+--------------------------------------------------------------+
| 到场状态                                                     |
| [待确认 v]                                                   |
| 实际来几人 [____]                                            |
| 儿童人数 [____]                                              |
| 需红包儿童人数 [____]                                        |
+--------------------------------------------------------------+
| 礼金金额(元) [___________]                                 |
| 礼金方式 [现金 v]                                            |
| 记录场景 [婚礼当天 v]                                        |
+--------------------------------------------------------------+
| 喜糖状态 [未发 v]                                            |
| 伴手礼状态 [未发 v]                                          |
| 儿童红包状态 [未发 v]                                        |
+--------------------------------------------------------------+
| 备注 [__________________________________________]           |
| [取消]                                      [保存修改]       |
+--------------------------------------------------------------+

保存确认页

+--------------------------------------------------------------+
| 请确认以下修改                                               |
+--------------------------------------------------------------+
| 到场状态:待确认 -> 确认出席                                 |
| 实际人数:0 -> 4                                             |
| 礼金金额:0 -> 2000                                          |
| 礼金方式:未填写 -> 现金                                     |
| [返回修改]                                [确认保存]         |
+--------------------------------------------------------------+

7. CSV 导入导出设计

7.1 导出

支持:

  • 导出全部
  • 导出当前筛选结果

当前实现约定:

  • 通过独立的 /csv/households/export 路由导出
  • 复用管理首页现有 query params(q / side / invite_status / attendance_status / sort_by / sort_order)生成“导出当前筛选结果”
  • 使用 Python 标准库 csv 在服务端生成响应
  • 使用 UTF-8 with BOM,兼容 Excel 打开中文 CSV
  • 使用 \r\n 行结束符

导出字段建议:

  • 户主姓名
  • 联系方式
  • 所属侧
  • 关系分类
  • 标签
  • 预计人数
  • 实际人数
  • 儿童数
  • 需红包儿童数
  • 礼金金额
  • 邀请状态
  • 到场状态
  • 伴手礼状态
  • 喜糖状态
  • 备注

7.2 导入流程

建议流程:

  1. 上传文件
  2. 服务端解析整份 CSV 并生成预览
  3. 校验表头、编码、字段值与选项编码
  4. 检查冲突
  5. 选择处理方式
  6. 确认导入
  7. 记录导入日志

当前实现约定:

  • 上传入口:/csv/households/import
  • 预览提交:POST /csv/households/import/preview
  • 确认提交:POST /csv/households/import/confirm
  • 上传文件限制:5 MB
  • 编码要求:UTF-8 / UTF-8 with BOM
  • 预览状态保存在 instance/csv_previews/*.json
  • 预览状态有 TTL,失效后必须重新上传

7.3 冲突检测建议

当前实现规则:

  • 强匹配:household_code
  • 次级冲突提示:head_name + side + phone

确认导入时支持两种处理方式:

  • skip_conflicts:只新增明确可创建的行,跳过编码更新候选与疑似冲突行
  • update_by_code:按户编码更新已存在户,同时新增明确可创建的行,仍跳过无编码命中的疑似冲突行

7.4 前端库建议

当前实现采用服务端方案,不依赖额外前端 CSV 库。

原因:

  • 需要与 SSR 查询、筛选结果和服务端导出保持一致
  • 需要统一复用服务端字段校验与选项编码校验逻辑
  • 需要在确认导入前形成可审计的服务端预览结果
  • 当前数据规模适合使用 Python 标准库 csv 直接实现

8. 防呆与可用性设计

8.1 通用原则

  • 默认只读
  • 危险操作二次确认
  • 删除建议软删除
  • 导入先预览
  • 重要提示不自动消失
  • 错误提示不只靠颜色

8.2 长辈友好原则

  • 单列布局
  • 字号至少 16px
  • 触控区域至少 44x44
  • 用词尽量口语化
  • 不用隐藏手势
  • 不用复杂拖拽

8.3 编辑冲突控制

建议加入 version 字段实现乐观锁。

用途:

  • 防止两个账号同时修改同一条记录时后保存者直接覆盖前者
  • 出现冲突时提示“该记录已被其他人修改,请刷新后重试”

9. 安全与实现边界

9.1 账号密码

虽然登录机制可以简单,但仍建议:

  • 密码哈希存储
  • 不明文保存密码
  • 停用账号禁止登录
  • 记录登录时间

9.2 数据删除

建议 MVP 阶段不做真正物理删除,优先软删除。

9.3 金额字段

礼金等金额字段在后端统一使用 Decimal 语义。

10. 分阶段实现建议

当前已完成的核心可用版本闭环

  • 基础项目结构、迁移、seed 与 .venv 开发工作流
  • 一键开发启动命令 flask dev-bootstrap,可自动迁移、初始化基础种子、初始化演示种子并启动服务
  • 登录 / 登出 / Session / 角色访问边界
  • 管理首页左侧边栏布局、统计前置、搜索独立一行、筛选排序独立一行
  • 管理端单条编辑页
  • 管理端新增一户最小创建流程
  • 管理端家庭成员管理(HouseholdMember)闭环:在户级编辑页支持成员新增、编辑、删除,并写入审计日志
  • 管理端礼金明细管理(gift_records)闭环:在户级编辑页支持礼金明细新增、编辑、软删除,自动重算户级礼金聚合字段并写入审计日志
  • 快速录入搜索、实时刷新、单独编辑页 / 新增页闭环
  • 分享搜索、实时刷新与单独编辑页闭环
  • 管理首页内聚统计摘要(独立统计报表页已下线)
  • light / dark 双主题切换
  • 压力测试数据命令 seed-stress
  • 账号管理页(列表、创建、编辑、重置密码、启停)
  • 审计日志页(列表、筛选、详情 before/after 快照)
  • 关键认证与业务动作审计日志
  • 管理首页搜索、组合筛选、排序、partial 动态刷新与筛选后统计摘要
  • 中文 / 拼音 / 首字母搜索
  • CSV 模板下载、导出全部、导出当前筛选、上传预览、校验、冲突处理、确认导入与导入审计日志
  • Python + Playwright E2E 基础设施与核心流程测试(管理员编辑、长辈录入、管理端新增户、家庭成员 CRUD、账号管理提交链路、审计日志筛选与详情、CSV 导入导出、认证与权限边界)
  • Linux 单机生产部署闭环:ProductionConfig、Python 3.13 容器镜像、Docker 入口脚本、环境变量示例、安装/部署/校验脚本、生产部署文档与运维文档
  • 当前最新一次本地全量测试:.venv/bin/python -m pytest tests107 passed

当前阶段边界

  • 当前已形成“核心可用版 + 生产部署交付”的阶段性基线,功能范围先暂停扩展
  • 后续工作以部署落地、文档保持同步、运维校验和必要缺陷修复为主
  • 如未来恢复功能迭代,可优先考虑桌席安排、更完整统计分析、历史往来能力,以及 CSRF 等正式发布前安全加固