4 Commits
Author SHA1 Message Date
laoyang 3a3c5890cc 安装向导 + PHP 8.5 兼容修复
新增 install.php:全新部署时自动接管,用户只需上传文件、打开站点即进入
安装向导。安装页自己做三件事:
  - 建 db/ 与 uploads/{posters,tickets,photos,avatars}/,权限调到 775
  - 逐目录真写临时文件实测可写,不靠 is_writable 猜
  - 建库、建管理员、写 db/.installed 锁、自删本文件

配套改动:
  - config.php 把安装检查前置于建库之前,未安装一律 302 到 install.php。
    原实现无条件调用 initDB(),导致「打开首页」就等于建库并种下 admin/123456。
  - lib/db.php 的 initDB(\$seedDefaultAdmin = true);安装向导传 false,
    管理员由用户自己设,绝不种弱口令。新增 isInstalled()。
  - login.php / nav.php 接上 siteName(),站点名称不再只存不用。
  - lib/settings.php 新增 siteName(),缺省回退「电影日志」。

PHP 8.5 兼容:
  - 删掉 fetch_movie.php 与 lib/storage/http.php 的 curl_close()。
    8.5 起该函数 deprecated,调用会往输出插一行警告,把 JSON 响应污染成
    「警告 + JSON」,前端 JSON.parse 失败 → 电影名联想功能完全失灵。
  - config.php 的 display_errors 由 1 改为 0(生产环境不应向浏览器吐错误),
    错误仍写日志;需要调试时设 MOVIELOG_DEBUG=1 打开。

线上验证(movie.yangyq.net,PHP 8.5.12):
  未安装时 / 、/login.php 均 302 → install.php;
  安装页 200、体检 10/10 通过、自动建好 5 个目录;
  安装后 install.php 404、管理员登录成功、首页 200 无报错;
  联想接口带会话请求「流浪地球/星际穿越/霸王别姬」均返回纯 JSON(首字符 {)。
2026-10-31 21:35:41 +08:00
laoyang 618409854c feat: 导航栏左上角的系统名做成「回到首页」链接
原来要回首页得先点开右侧折叠菜单再点「首页」(两步操作,在手机上尤其别扭),
现在点左上角系统名一下就能到。

nav.php
- <div class="nav-brand"> 改为 <a href="index.php" class="nav-brand">,
  并补上 title 与 aria-label(无障碍:屏幕阅读器能读出这是个「回到首页」链接)。
- 因为是原生 <a>,Ctrl/中键点击可在新标签页打开,不再需要 JS。

css/style.css
- 给 .nav-brand 加点击热区:padding + 等量负 margin 抵消,
  保证文字的视觉位置与改动前完全一致(600/768px 两个断点下 navbar 的
  padding 变窄时,两者依然自然抵消)。
- 加 hover 底色反馈,让「这是可点的」有视觉提示。
- 加 :focus-visible 轮廓:键盘 Tab 聚焦时可见,鼠标点击时不出现,
  避免多出一层多余描边。

验证
- nav.php 语法通过(双 PHP 版本 7.4 / 8.1),内联 JS 用「模拟 PHP 渲染」方式
  做 node --check 通过。
- CSS 大括号配平(83/83)。
- fetch_movie_test.py 端到端 79 项、storage_test.py 回归 196 项全通过。
- A/B 渲染比对 47 个响应:45 个含导航的页面有差异,但**差异只有那一行**
  <div> → <a>(含注释与闭合标签),其余内容逐字节一致;不含导航的
  登录失败页与退出页完全未变。
- 线上双 PHP 版本真实渲染导航栏:链接形态、title/aria-label、折叠菜单均在,
  无 PHP 报错泄漏;公网 style.css 与本地 sha256 一致。
2026-09-22 08:01:00 +08:00
laoyang 673a8abf78 feat: 云端图片存储(OSS/COS/七牛)+ 服务端代理读取,并补全文档
新增存储抽象层:图片可存本地或云对象存储,切换存储方式不影响已有图片。

存储层
- lib/storage.php 存储门面:本地优先读取、URL 生成、上传/删除、批量迁移、
  服务端读取(storageRead)与访问方式判定(storageAccessMode)
- lib/storage/{local,oss,cos,qiniu}.php 四种后端驱动;http.php 共用 cURL 客户端;
  meta.php 实例元数据鉴权(ECS RAM 角色 / CVM CAM 角色,免密钥)
- lib/settings.php 设置读写;云厂商密钥以 enc:v1:gcm 密文落库(db/.settings.key)
- 文件名改为内容指纹(md5 前 12 位_6 位随机[_角色].ext),内容不变则名不变,
  因此可下发 immutable 长缓存

两种图片访问方式
- 直连存储:storageUrl() 产出对象存储地址(桶需允许匿名读)
- 服务端代理:media.php 鉴权后由服务器签名读取并同源回吐,桶可保持私有
  · 未登录返回 403 纯文本(刻意不用 302,否则 <img> 会拿到一段 HTML)
  · 对象键白名单防目录穿越;Content-Type 按扩展名白名单决定 + nosniff(防同源 XSS)
  · 透传 If-None-Match / Range,上游 304 直接短路;private + immutable 长缓存
  · 鉴权后立即 session_write_close(),避免排他会话锁把并行图片请求串行化

内网 endpoint 判定(修「开了代理后慢到不可用」)
- 内网地址只在与桶同地域的 ECS 上可路由;跨地域时 DNS 能解析但 TCP 连不通,
  无脑「内网优先」会让每次读取先白等满一次连接超时(默认 10 秒)
- 改为先比本机 ECS 地域与桶地域(零成本、结论确定)→ 不同即跳过;
  结论落缓存;内网只给 2 秒连接超时,传输层失败立刻标记且短期内不再重试
- 「测试连通性」升级为 PUT → GET 回读 → DELETE,并显式报出内网判定结论

界面与页面
- settings.php 系统设置页:驱动选择、各云配置、图片访问方式、连接测试、迁移进度
- storage_api.php 提供 stat / migrate / test 三个 JSON 接口
- detail / edit / annual_report / persons 原先直接拼本地路径(file_exists / '/'.$path),
  迁云后会整块不渲染或 404,统一改为走存储门面
- js/main.js 增加破图占位兜底;config.php 统一下发 Referrer-Policy: same-origin

文档
- README 新增《图片存储与对象存储》整章:设计约定、两种访问方式取舍表、
  直连存储逐项配置说明(桶 ACL / 防盗链白名单 / 跨域 CORS 与 crossorigin /
  强制下载 / endpoint 地域 / 自定义域名)与上线检查清单、服务端代理实现要点、
  迁移步骤、12 条踩坑记录、可直接复制的排障命令手册
2026-09-21 22:06:02 +08:00
laoyang 40e1bc5e56 feat: 完善电影记录系统源码与项目文档
功能模块:
- 观影记录:列表(瀑布流)/ 添加 / 编辑 / 详情 / 删除,删除时同步清理图片文件
- 首页快捷搜索 + 高级搜索(片名、影院、观影人、日期区间组合筛选)
- 影院管理:三段式信息 + 50 种预设配色,删除已引用影院时二次确认
- 观影人管理:头像裁剪上传 + 专属配色,已引用者禁止删除
- 图片处理:Cropper.js 裁剪票根/海报/观影照片/头像,多图上传,首字母 SVG 头像兜底
- OMDb 自动获取影片信息(fetch_movie.php 代理,返回 JSON)
- CSV 批量导入:UTF-8/GBK 自动识别,影院去重,事务保护,逐行错误报告
- 统计仪表板:总览 + 按影院/观影人/年份/近 12 个月排行
- 年度报告:按账号关联的观影人视角生成,含 Chart.js 月度与星期分布图
- 多用户:账号增删改、账号与观影人绑定、修改自己的用户名与密码
- 登录鉴权:Session + password_hash,全站页面登录校验

文档:
- 重写 README:功能特性、技术栈、目录结构、数据库结构、部署与使用指南、接口说明、CSV 格式、安全注意事项、已知限制
- 新增 .gitignore,排除 db/ 与 uploads/ 等运行时数据
2026-09-21 08:20:56 +08:00