# 轻鸽源码商城系统 · 开发文档

> 域名：qght.cn ｜ 服务器：198.44.176.67 ｜ 技术栈：PHP + 文件存储
> 源码商城全部接口仅限 APP 前端对接（token 签名保护），不对外开放给其他开发者
> 所有源码商城功能均针对**后台账号**（`userdirectory/{账号}/admin/`），与各后台的子用户无关

> **当前版本 v2.1**：v2.0 全量开发（16 接口 + 管理后台 + UI 重设计）之上，新增**管理后台移动端全面适配**与 **10 项 Bug 修复**（详见「六、v2.1 更新记录」）。

---

## 一、本次修改总览

### 1.1 新增文件

| 文件 | 说明 |
|------|------|
| `dockingshop_common.php` | 源码商城公共库：统一输出/鉴权/封禁/频率限制/敏感词过滤/咕咕信件/购买记录/邮件通知 |
| `dockingshop_comment.php` | 发表源码评论 |
| `dockingshop_commentecho.php` | 评论列表（分页，语义同源码列表） |
| `dockingshop_commentdelete.php` | 删除评论（本人或源码作者） |
| `dockingshop_praise.php` | **源码点赞/取消点赞（融合切换式：请求一次点赞，再请求取消）** |
| `dockingshop_commentpraise.php` | 评论点赞/取消点赞（同为融合切换式） |
| `dockingshop_report.php` | 举报源码/举报评论 |
| `dockingshop_my.php` | 我发布的源码（含审核状态） |
| `dockingshop_buylist.php` | 我的购买记录 |
| `dockingshop_delete.php` | 删除自己发布的源码 |
| `dockingshop_change.php` | 修改自己发布的源码信息 |
| `dockingshop_info.php` | 源码详情（含点赞/购买状态） |
| `dockingshop/admin/index.php` | **网页管理后台**（登录 + 概览/审核/源码/评论/举报/封禁六大模块） |
| `dockingshop/admin/api.php` | 管理后台接口（会话鉴权，22 个动作） |
| `dockingshop/admin/config.php` | 管理后台账号配置 |
| `dockingshop/{down,reject,buy,report,frozen}/.htaccess` 等 | 数据目录防直链访问保护 |

### 1.2 优化（重写）的已有文件

| 文件 | 优化内容 |
|------|----------|
| `dockingshop_list.php` | 新增 praise 点赞数、commentnum 评论数字段；**付费源码对未购买者隐藏 url**（防盗链）；per_page 上限 50 防拉库；保持原分页语义不变（前端无感） |
| `dockingshop_search.php` | 同上（praise/commentnum 字段 + url 隐藏） |
| `dockingshop_up.php` | 新增敏感词过滤（论坛同款词库）、20秒频率限制（同论坛发帖）、VIP 到期校验、标题50字/介绍2000字限制、售价正整数校验、时间改用服务器时间（防伪造排序） |
| `dockingshop_upload.php` | **新增后台账号鉴权**（原版无鉴权任何人可传）；文件大小显示保留一位小数 |
| `dockingshop_view.php` | 浏览数改用文件锁原子自增（防并发丢计数） |
| `dockingshop_download.php` | 下载数原子自增；**新增下载资格校验**（免费/本人/已购买才返回 url，否则返回 403 + 源码价格） |
| `dockingshop_money.php` | **重写为原子交易**：双文件加锁转账（qg_lock_two_update）杜绝并发双击重复扣款；锁内幂等（已购买直接返回 url）；购买记录落盘（`dockingshop/buy/{账号}.json`）；咕咕信件通知买卖双方；兼容旧版 admin2 参数 |

### 1.3 UI 重设计（与官网设计语言统一）

全部页面改为官网编辑风格：象牙白 `#F0EEE6` 背景、墨黑 `#191919` 文字、陶土红 `#CC785C` 强调色、衬线标题（Georgia/宋体系）、发丝线分隔、充足留白、精简文案与图标、深色模式（localStorage `qingge-theme`）。

| 文件 | 风格 |
|------|------|
| `dockingshop/admin/index.php` | 管理后台（全新）：顶栏导航 + 数据卡片 + 表格 + 弹窗，深浅双主题 |
| `app1.php` | 卡片式下载页（居中布局） |
| `app2.php` | 编辑部式详情页（大标题 + 元信息行 + 截图网格） |
| `app3.php` | 极简式下载页（紧凑居中） |
| `404.html` / `404-2.html` / `404-3.html` | 统一 404（依次为：页面不存在 / 应用不存在 / 资源不存在），10秒自动回首页 |

### 1.4 咕咕信件自动通知（echonewmsg.php 可读）

所有信件写入 `userdirectory/{账号}/newmsg`，格式 `[内容|Y-m-d H:i]`，新信件置顶：

| 触发场景 | 信件内容示例 |
|----------|--------------|
| 购买源码（买家） | `[您已花费 "10" 咕币购买《测试》源码，祝鸽鸽一切顺利~\|2026-04-15 19:34]` |
| 购买源码（卖家） | `[用户: "1001" 已花费 "10" 咕币购买您发布的《测试》源码，祝鸽鸽一切顺利~\|2026-04-15 19:34]` |
| 源码被点赞 | `[用户: "1002" 赞了您发布的《测试》源码\|...]` |
| 评论被点赞 | `[用户: "1002" 赞了您在《测试》源码下的评论\|...]` |
| 源码被评论 | `[用户: "1002" 评论了您发布的《测试》源码\|...]` |
| 审核通过 | `[您发布的《测试》源码已通过审核，已上架源码商城\|...]` |
| 审核驳回 | `[您发布的《测试》源码未通过审核，原因: xxx，可修改后重新发布\|...]` |
| 源码被下架 | `[您发布的《测试》源码已被下架，原因: xxx\|...]` |
| 源码重新上架 | `[您发布的《测试》源码已重新上架源码商城\|...]` |
| 源码被管理员删除 | `[您发布的《测试》源码已被删除，原因: xxx\|...]` |
| 管理员修改源码 | `[管理员修改了您发布的《测试》源码信息\|...]` |
| 评论被管理员删除 | `[您在《测试》源码下的评论已被删除，原因: xxx\|...]` |
| 被封禁 | `[您已被封禁源码商城功能，封禁至 2026-05-01 10:00，原因: xxx\|...]` |
| 解除封禁 | `[您的源码商城功能封禁已被解除，欢迎继续使用\|...]` |

---

## 二、通用调用规范

### 2.1 请求签名（与全站一致）

每个源码商城接口调用时必须携带：

```
timestamp = 当前毫秒时间戳（与服务器时差 ±60秒内）
sign      = md5( token常量 + timestamp )
```

`token` 常量定义在服务端 `config.php`（即 `define('token', 'qght_qingge_tomato_430')`），仅 APP 前端持有，不对外公开。支持 GET / 表单 POST / JSON POST 三种方式。

### 2.2 统一鉴权参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| admin | string | 是 | 后台账号（纯数字） |
| pass | string | 是 | 后台密码 |

### 2.3 统一响应格式

```json
{ "code": 200, "msg": "提示消息", "data": ... }
```

| code | 含义 |
|------|------|
| 200 | 成功 |
| 400 | 参数错误 / 内容不合规 / 重复操作 |
| 403 | 密码错误 / 账号过期 / 被封禁 / 频繁操作 / 未购买 |
| 404 | 账号、源码、评论不存在 / 无数据 / 已到头 |
| 500 | 服务器写入失败 |

### 2.4 与论坛系统的实现对应关系（前端套用参考）

| 论坛接口 | 源码商城接口 | 参数变化 |
|----------|--------------|----------|
| forum.php（发帖） | dockingshop_up.php | `user/mods` 移除；`title/content` 保留；新增 `url/language/size/money/icon/pic1-3` |
| forumcomment.php（评论） | dockingshop_comment.php | `id`→`dockingid`；`user/mods` 移除 |
| forumecho.php（帖子列表） | dockingshop_commentecho.php | `id`→`dockingid`；新增 `page/per_page` |
| praise.php（点赞） | dockingshop_praise.php | `user2` 移除（目标是 `dockingid`）；**改为切换式** |
| 帖子删除 | dockingshop_delete.php / dockingshop_commentdelete.php | 按 `dockingid`/`commentid` 删除 |
| adminseal.php（封号） | 管理后台"用户封禁" | 商城级封禁，不影响后台账号本体 |

### 2.5 频率限制（同论坛量级）

发布源码 20 秒 / 评论 10 秒 / 点赞 5 秒 / 举报 30 秒 / 修改 10 秒。超限返回 `403 频繁操作，请N秒后重试`。

### 2.6 数据目录结构

```
dockingshop/
├── audit/    待审核源码 {id}.json
├── docking/  已上架源码 {id}.json
├── down/     已下架源码 {id}.json
├── reject/   审核未通过 {id}.json（含 reason 字段）
├── comment/  评论 {源码id}.json（JSON数组，新评论在前）
├── file/     上传的源码文件（可直链下载）
├── buy/      购买记录 {账号}.json
├── report/   举报 source_{id}.json / comment_{评论id}.json
├── frozen/   操作频率 {账号}
└── admin/    网页管理后台
    ├── index.php / api.php / config.php
    ├── sessions/   后台会话（禁止Web访问）
    └── data/ban/   商城封禁 {账号}（存到期时间戳）
```

源码 JSON 字段：`title, content, icon, pic1, pic2, pic3, url, language, time, money, size, view, download, praise, praiselist[], user`（驳回另含 `reason`，修改另含 `updatetime`）。

---

## 三、源码商城接口参数详情（16 个）

### 3.1 dockingshop_list.php — 源码列表

**提交参数**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| admin | string | 是 | 后台账号 |
| pass | string | 是 | 后台密码 |
| page | int | 否 | 页码，默认 1 |
| per_page | int | 否 | 每页数量，默认 10，上限 50 |
| language | string | 否 | 语言筛选（iapp/lua/web 等，留空为全部） |

**返回参数**（`data` 为数组，每项字段如下）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 源码ID |
| title / content | string | 标题 / 介绍 |
| icon / pic1 / pic2 / pic3 | string | 图标 / 截图地址 |
| url | string | 下载地址（**免费源码或本人发布才返回，付费未购买返回空字符串**） |
| time | string | 发布日期 `Y-m-d` |
| size | string | 大小（如 5.2MB） |
| view / download | int | 浏览数 / 下载数 |
| praise | int | 点赞数 ★新增 |
| commentnum | int | 评论数 ★新增 |
| name / user / txurl | string | 作者昵称 / 账号 / 头像 |
| money | string | 售价咕币（空或0为免费） |
| language | string | 语言分类 |

示例返回：`{"code":200,"msg":"获取成功","data":[{...},{...}]}`
其他：`404 暂无优质源码~` / `404 已经到头啦~` / `400 页码无效`

### 3.2 dockingshop_search.php — 源码搜索

**提交参数**：`admin`、`pass` 必填；`search`（关键词，模糊匹配标题）必填。
**返回参数**：与 3.1 的 data 字段完全一致（不分页，返回全部匹配项）。
其他：`404 未找到相关源码~`

### 3.3 dockingshop_info.php — 源码详情 ★新增

**提交参数**：`admin`、`pass`、`dockingid`（源码ID）。

**返回参数**（`data` 为对象）：3.1 全部字段，另含

| 字段 | 类型 | 说明 |
|------|------|------|
| free | bool | 是否免费 |
| own | bool | 是否本人发布 |
| isbuy | bool | 是否有下载资格（免费/本人/已购买为 true） |
| ispraise | bool | 当前账号是否已点赞该源码 |

其他：`404 源码不存在` / `404 源码已下架`

### 3.4 dockingshop_up.php — 发布源码（进入审核）

**提交参数**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| admin / pass | string | 是 | 后台账号密码（要求账号未过期、未被封禁） |
| title | string | 是 | 标题，≤50字，自动过滤敏感词 |
| content | string | 是 | 介绍，≤2000字，自动过滤敏感词 |
| url | string | 是 | 下载地址（来自 dockingshop_upload.php 返回） |
| language | string | 是 | 语言分类（iapp/lua/web 等） |
| size | string | 是 | 大小（来自 upload 返回） |
| money | string | 否 | 售价咕币，正整数；留空/0 为免费 |
| icon / pic1 / pic2 / pic3 | string | 否 | 图标/截图地址，传 "null" 视为空 |
| time | string | 否 | 兼容参数（已忽略，统一使用服务器时间） |

**返回参数**：`{"code":200,"msg":"发布成功，耐心等待审核噢~"}`
其他：`400 缺少必要参数/长度超限/售价需为有效正整数咕币` / `403 频繁操作，请N秒后重试（20秒冷却）` / `403 后台账号过期无法操作` / `403 您已被封禁源码商城功能，封禁至 xxx`

### 3.5 dockingshop_upload.php — 上传源码文件

**提交参数**：`admin`、`pass`（★新增鉴权）；`file`（文件域，≤10MB，禁止 php/asp/jsp/exe 等可执行类型）。

**返回参数**：`{"code":200,"msg":"上传成功","url":"https://qght.cn/dockingshop/file/xxx.zip","size":"5.2MB","time":"2026-04-15"}`

### 3.6 dockingshop_view.php — 浏览数 +1

**提交参数**：`admin`、`pass`、`dockingid`。
**返回参数**：`{"code":200,"msg":"浏览数+1","view":123}`

### 3.7 dockingshop_download.php — 获取下载地址并计数 ★强化

**提交参数**：`admin`、`pass`、`dockingid`。

**返回参数**

- 有资格（免费/本人/已购买）：`{"code":200,"msg":"下载数+1","url":"...","download":456}`
- 无资格：`{"code":403,"msg":"请先购买该源码","data":{"money":"10"}}`（前端用 money 弹起购买确认）

其他：`404 源码已下架`

### 3.8 dockingshop_money.php — 咕币购买 ★重写

**提交参数**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| admin / pass | string | 是 | 买家后台账号密码 |
| dockingid | string | 是 | 源码ID |
| money | string | 是 | 支付咕币数（必须与源码售价一致，防篡改） |
| admin2 | string | 否 | 兼容旧版收账人参数；传入时必须与源码发布者一致 |

**返回参数**

- `{"code":200,"msg":"购买成功","url":"...","dockingid":"..."}`
- 重复购买（幂等）：`{"code":200,"msg":"您已购买过该源码","data":{"url":"...","dockingid":"..."}}`

其他：`400 咕币不足` / `400 金额与源码价格不符` / `400 不能购买自己发布的源码` / `404 源码已下架`
副作用：买家扣款、卖家加款（原子）、写入购买记录、双方咕咕信件。

### 3.9 dockingshop_comment.php — 发表评论 ★新增

**提交参数**：`admin`、`pass`、`dockingid`、`content`（≤500字，敏感词过滤）。

**返回参数**：`{"code":200,"msg":"评论成功","commentnum":12}`（该源码最新评论总数）
其他：`403 频繁操作，请N秒后重试（10秒冷却）` / `404 源码已下架`
副作用：源码作者收到咕咕信件（自己评论自己不发）。

### 3.10 dockingshop_commentecho.php — 评论列表 ★新增

**提交参数**：`admin`、`pass`、`dockingid`、`page`（默认1）、`per_page`（默认10，上限50）。

**返回参数**（`data` 数组，新评论在前）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 评论ID（用于点赞/删除/举报） |
| user / name / txurl | string | 评论者账号 / 昵称 / 头像 |
| content | string | 评论内容 |
| time | string | `Y-m-d H:i` |
| praise | int | 该评论点赞数 |
| ispraise | bool | 当前账号是否已赞该评论 |

分页语义与源码列表一致：`404 暂无评论~` / `404 已经到头啦~`

### 3.11 dockingshop_commentdelete.php — 删除评论 ★新增

**提交参数**：`admin`、`pass`、`dockingid`、`commentid`。
权限：评论作者本人 或 源码发布者。
**返回参数**：`{"code":200,"msg":"删除成功","commentnum":11}`

### 3.12 dockingshop_praise.php — 源码点赞/取消（融合切换式）★新增

**提交参数**：`admin`、`pass`、`dockingid`。

**行为**：请求一次 = 点赞；再请求一次 = 取消点赞；以此类推（不能给自己发布的源码点赞）。

**返回参数**

```json
{"code":200,"msg":"点赞成功","status":"liked","praise":5}
{"code":200,"msg":"已取消点赞","status":"unliked","praise":4}
```

副作用：点赞时源码作者收到咕咕信件；取消点赞不发信件。

### 3.13 dockingshop_commentpraise.php — 评论点赞/取消（融合切换式）★新增

**提交参数**：`admin`、`pass`、`dockingid`、`commentid`。
**返回参数**：同 3.12（`status`/`praise` 为该评论的状态与计数）。

### 3.14 dockingshop_report.php — 举报 ★新增

**提交参数**

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| admin / pass | string | 是 | 举报人 |
| type | string | 是 | `source` 举报源码 / `comment` 举报评论 |
| dockingid | string | 是 | 源码ID |
| commentid | string | type=comment时必填 | 评论ID |
| reason | string | 是 | 举报理由，≤200字 |

**返回参数**：`{"code":200,"msg":"举报成功，感谢您的反馈，我们会尽快处理"}`
其他：`400 您已举报过，请耐心等待处理` / `400 不能举报自己发布的内容哦` / `403 频繁操作（30秒冷却）`

### 3.15 dockingshop_my.php — 我发布的源码 ★新增

**提交参数**：`admin`、`pass`。
**返回参数**（`data` 数组）：3.1 全部字段（本人源码 url 始终可见），另含

| 字段 | 说明 |
|------|------|
| status | `"0"`待审核 / `"1"`已上架 / `"2"`已下架 / `"3"`未通过 |
| reason | status=3 时的驳回原因 |

无数据：`404 您还没有发布过源码~`

### 3.16 dockingshop_buylist.php — 我的购买记录 ★新增

**提交参数**：`admin`、`pass`。
**返回参数**（`data` 数组）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | string | 源码ID |
| title / money / time | string | 标题 / 花费咕币 / 购买时间 |
| user / name / txurl | string | 卖家账号 / 昵称 / 头像 |
| url | string | 下载地址（源码已下架或被删时为空） |
| size / language | string | 大小 / 语言 |
| status | string | `"1"`已上架可下载 / `"2"`已下架 / `"3"`源码已被删除 |

无数据：`404 暂无购买记录~`

### 3.17 dockingshop_change.php — 修改自己的源码 ★新增

**提交参数**：`admin`、`pass`、`dockingid`；可选修改字段 `title`(≤50字)、`content`(≤2000字)、`url`、`language`、`size`、`icon`、`pic1`、`pic2`、`pic3`、`money`（正整数/空为免费）。仅传入的字段被修改，其余保持不变。仅限已上架的本人源码。

**返回参数**：`{"code":200,"msg":"修改成功"}`

### 3.18 dockingshop_delete.php — 删除自己的源码 ★新增

**提交参数**：`admin`、`pass`、`dockingid`。
可删除处于待审核/已上架/已下架/未通过状态的**本人**源码；评论与举报记录一并清理。
**返回参数**：`{"code":200,"msg":"删除成功"}`

---

## 四、网页管理后台（dockingshop/admin/）

### 4.1 访问与登录

- 地址：`https://qght.cn/dockingshop/admin/`
- 默认账号：`admin` ／ 默认密码：`qingge2026`
- **部署后请立即登录 → 右上角 🔑 图标 → 修改密码**（自动生成 bcrypt 哈希保存至 `dockingshop/admin/data/credentials.php`）
- 登录失败 5 次锁定 15 分钟；会话 2 小时无操作自动过期；Cookie 为 HttpOnly + SameSite=Strict（HTTPS 下自动加 Secure）

### 4.2 功能模块

| 模块 | 功能 |
|------|------|
| 概览 | 待审核/上架/举报/评论/点赞/下载/封禁/账号 数量统计 + 待办清单 |
| 源码审核 | 待审列表（搜索）、详情查看、**通过上架**（信件通知）、**驳回**（填原因，信件通知） |
| 源码管理 | 已上架/已下架/已驳回三栏；搜索；**编辑任意字段**（信件通知）；**下架**（填原因）、重新上架、**删除**（可同时快捷封禁作者） |
| 评论管理 | 按源码ID查看全部评论；删除评论（可同时封禁评论者，信件通知） |
| 举报处理 | 被举报源码与评论列表（含举报人/理由/时间）；**驳回举报 / 删除被举报内容 + 可选封禁作者** |
| 用户封禁 | 按账号/昵称搜索后台账号（显示咕币/VIP到期/源码数/封禁状态）；**快捷封禁**（1/7/30/永久 + 原因，信件通知）、解封（信件通知） |

被封禁的后台账号无法进行源码商城任何操作（发布/评论/点赞/购买），浏览列表同样受限；封禁不影响其后台账号本体功能。

### 4.3 后台接口动作（api.php，会话鉴权，无需 token 签名）

`login` `logout` `session` `password_change` `stats` `audit_list` `audit_pass` `audit_reject` `source_list` `source_edit` `source_takedown` `source_republish` `source_delete` `comment_list` `comment_delete` `report_list` `report_resolve` `user_search` `user_ban` `user_unban`

---

## 五、部署说明

### 5.1 增量部署（推荐）

将 `增量代码.zip` 内全部文件按原路径覆盖/新增到站点根目录即可，无需改动其他文件：

```
覆盖: dockingshop_list.php  dockingshop_search.php  dockingshop_up.php
      dockingshop_upload.php  dockingshop_view.php  dockingshop_download.php
      dockingshop_money.php  app1.php  app2.php  app3.php
      404.html  404-2.html  404-3.html
新增: dockingshop_common.php  dockingshop_comment.php  dockingshop_commentecho.php
      dockingshop_commentdelete.php  dockingshop_praise.php  dockingshop_commentpraise.php
      dockingshop_report.php  dockingshop_my.php  dockingshop_buylist.php
      dockingshop_delete.php  dockingshop_change.php  dockingshop_info.php
      dockingshop/admin/（整个目录）
      dockingshop/down/  dockingshop/reject/  dockingshop/buy/  dockingshop/report/
      dockingshop/frozen/（含 .htaccess）
      dockingshop/docking/.htaccess 等各目录 .htaccess
```

### 5.2 注意事项

1. **目录权限**：确保 `dockingshop/` 及子目录对 PHP 可写（0777），系统会自动创建缺失目录。
2. **.htaccess**：若服务器为 Apache，数据目录 JSON 已禁止直链；若为 Nginx，请在站点配置中补充：
   ```nginx
   location ~ ^/dockingshop/(docking|audit|down|reject|comment|report|buy|frozen|admin/data|admin/sessions)/.*\.(json)$ { deny all; }
   ```
3. **前端适配提示**：
   - 付费源码在列表/搜索中 `url` 为空，请改为进入详情后调用 `dockingshop_info.php` 判断 `isbuy`，未购买走 `dockingshop_money.php` → 成功后用返回的 `url` 或 `dockingshop_download.php`。
   - 点赞按钮为切换式：根据返回的 `status` 字段（`liked`/`unliked`）切换 UI，`praise` 为最新计数。
   - 评论分页与源码列表分页逻辑完全一致，可直接套用。
4. **兼容性**：旧版前端不传 `per_page`/`type` 等新参数时全部按默认值处理；`admin2`/`time` 等旧参数保留兼容，不会报错。

---

## 六、v2.1 更新记录（管理后台移动端适配 + Bug 修复）

> 涉及文件：`dockingshop/admin/index.php`（移动端适配 + 前端修复）、`dockingshop/admin/api.php`（后端修复）。
> 已部署过 v2.0 的站点，仅需覆盖这两个文件即可升级到 v2.1。

### 6.1 移动端适配优化（index.php）

| 项目 | 修复内容 |
|------|----------|
| **表格横向滚动（关键）** | `.table-wrap` 由 `overflow:hidden` 改为 `overflow-x:auto`（表格内容 `min-width:600px`）。修复手机上 6 列表格右侧操作列（通过/驳回/编辑/下架/删除/封禁）被整体裁剪、**完全无法操作**的问题；概览"待办事项"表（3 列）保持 `min-width:0` 不产生横向滚动 |
| **iOS 输入聚焦缩放** | 手机端（≤640px）输入框/下拉/文本域字号提升至 **16px**，修复 iOS Safari 聚焦输入框时自动放大页面且无法还原 |
| **刘海屏安全区** | `viewport-fit=cover` + `env(safe-area-inset-top/bottom)`：顶栏避让刘海、页面底部避让手势条 |
| **统计卡双列布局** | 手机端概览 8 张统计卡改为 **2 列紧凑布局**（原单列全宽需滚动 8 屏），卡片内边距与字号同步收紧 |
| **顶栏导航紧凑化** | 移动端导航标签行全宽横向滑动（负外边距顶到屏幕边缘）、标签内边距加大到 40px 触控标准、顶栏高度 115px→110px |
| **工具行自适应** | 各页搜索框 / 源码ID输入框（原固定 240px/280px/300px）移动端 **100% 自适应宽度**，配合按钮不再溢出视口 |
| **弹窗移动端优化** | 手机端弹窗内边距收紧（32/34→24/20 与 18/14 两档）、底部操作按钮 `flex` 伸展为宽按钮易点按；`max-height` 增加 `dvh` 降级，修复 iOS 地址栏遮挡导致按钮不可见 |
| **登录页高度** | `min-height:100dvh` 降级方案，小屏手机（iPhone SE）登录按钮不再被地址栏遮挡 |
| **Toast 居中** | 手机端提示条改为全宽居中展示，避开刘海安全区（原右上角小字易被状态栏遮挡） |
| **触屏体验** | `@media (hover:none)` 禁用按钮/卡片悬停位移残留（触屏点按后按钮"卡"在悬起状态）；源码状态筛选 chips 行支持横向滑动 |
| **表格内容保护** | 移动端表格字号 0.8rem + 单元格内边距 10px + 长标题/昵称 `word-break`，滚动查看时不再挤压换行错乱 |

### 6.2 Bug 修复（api.php 后端）

| # | Bug 现象 | 修复 |
|---|----------|------|
| 1 | **举报处理选"永久封禁"失效**：`report_resolve` 中 `intval('forever')=0`，勾选永久封禁后被静默忽略，实际未封禁 | 解析 `ban_days=forever` → 36500，正确写入永久封禁并发信件 |
| 2 | **举报详情看不到付费源码下载地址**：`report_list` 源码举报组装时缺 `showAllUrl` 参数，付费源码 `url` 按付费策略被隐藏，管理员无法核实被举报内容 | 补第 5 参 `true`，管理后台 url 始终可见 |
| 3 | 源码列表搜索标题大小写敏感（`mb_strpos`）而作者不敏感（`mb_stripos`），搜大写关键词找不到小写标题 | 统一为 `mb_stripos` |
| 4 | 用户搜索昵称匹配同样大小写不一致 | 统一为 `mb_stripos` |

### 6.3 Bug 修复（index.php 前端）

| # | Bug 现象 | 修复 |
|---|----------|------|
| 5 | **必填原因被放行且弹窗先关闭**：驳回/下架/删除/封禁等弹窗点击"确认"后立即关闭弹窗才执行校验，未填原因时只弹 toast，输入场景全部丢失，必须重新打开弹窗从头填写 | 关闭弹窗前先校验必填原因，未通过保持弹窗打开并自动聚焦输入框 |
| 6 | **已删源码的举报渲染出 undefined**：被举报源码已被删除时（missing 分支），详情卡渲染"浏览 undefined · 下载 undefined · 点赞 undefined · 评论 undefined"及空白的售价/时间字段 | missing 时改显示占位提示卡（"该源码已被删除，仅存举报记录"）；`detailBody` 全字段兜底（`x || 0` / `x || '-'`） |
| 7 | **旧系统整页白屏**：`sourceDelete` 使用可选链 `?.`（ES2020），iOS 13.4 以下 / 旧版安卓 WebView 直接解析失败，整个管理后台 JS 崩溃白屏 | 改写为 `find()` + 显式判空的 ES5 兼容写法 |
| 8 | **onclick 内联参数转义缺陷**：`esc()` 仅做 HTML 转义，输出到 `onclick="fn('...')"` 的 JS 上下文后，`&#39;` 被浏览器解码回单引号，账号名含引号时点击按钮触发 JS 语法错误（同时是潜在注入点） | 新增 `jsq()`（先 JS 转义反斜杠/单引号，再 HTML 转义），全部 12 处内联参数替换 |
| 9 | 顶栏品牌链接 `href="#"` 点击后跳回页面顶部 | 改为非链接 `<span>`，消除误触滚动 |
| 10 | 主题/密码/退出图标按钮无 `aria-label`，读屏软件显示为空按钮 | 补充无障碍标签 |

### 6.4 验证方式与结果

- **桌面 1280px 回归**：七个页面 + 深色模式 + 全部弹窗，无横向溢出、无 JS 报错、表格不出现横向滚动条（桌面布局零破坏）。
- **移动端 390px / 375px 实测**：登录页、概览（统计卡 2 列）、审核/源码/评论/用户（表格可横滑、滚动后操作按钮完全可见可点）、举报处理（卡片紧凑）、编辑弹窗（按钮全宽）、Toast 居中。
- **交互回归**：登录 / 退出 / 封禁（含永久与时长选择）/ 解封 / 驳回必填校验（弹窗保持打开）/ 编辑保存 / 举报三种处理方式，全部通过。
- **静态检查**：25 个 PHP 文件括号配对 / 引号闭合 / 函数定义检查全部通过。


## 七、v2.2 更新记录（本次交付）

本章为 v2.2 增量更新说明，涉及 21 个文件修改。已部署 v2.1 的站点按"增量代码.zip"覆盖对应文件即可完成升级。

### 7.1 Bug 修复（后端 API）

| # | 问题 | 原因分析 | 修复方案 | 涉及文件 |
|---|------|----------|----------|----------|
| 1 | **源码商城系列接口 token 验证异常，前端偶发 `timestamp null`**（其他带 token 的接口均正常） | `token.php` 仅从 `$_REQUEST` 读取 `timestamp`/`sign`。PHP **不会自动解析 JSON 请求体**，前端对源码商城接口以 `application/json` 提交时（本系统接口文档声明支持 JSON body），签名参数在 JSON 体内，`$_REQUEST` 中为空 → 403"缺少必要参数"。其他接口前端一直以 GET/表单方式调用故不受影响，造成"仅源码商城接口异常"的现象 | ① `token.php` 增加兜底：`$_REQUEST` 缺参时自动从 `application/json` 请求体补读 `timestamp`/`sign`（对全部 185 个带 token 接口生效，GET/表单提交行为完全不变）；② 18 个 `dockingshop_*.php` 统一引导顺序——JSON/表单解析 `qg_ds_parse_input()` 提前到 `token.php` 之前，保证签名与业务参数均可从任意提交方式（GET / 表单 / JSON）读取 | `token.php`、全部 18 个 `dockingshop_*.php` |
| 2 | **获取源码系列接口的 `money` 值不规范**：免费源码返回空字符串 `""`，存量数据可能为字符串/数字混杂 | `qg_ds_source_summary` 原样返回存储值 | 新增统一出口函数 `qg_ds_money_out()`：**不收费 / 为空 / 非法值 → 返回数字 `0`；收费 → 返回正整数**。已应用到源码列表/搜索/详情/我的/购买记录/下载提示全部出口；新增 `qg_ds_money_free()` 统一免费判定，购买接口价格一致性校验同步加固（兼容存量字符串/数字格式） | `dockingshop_common.php`、`dockingshop_buylist.php`、`dockingshop_download.php`、`dockingshop_money.php` |
| 3 | 购买接口价格比对使用严格字符串比较，存量数据 `money` 为数字类型时存在比对隐患 | `(string)` 强转比对 | 改为数值比较 `(int)$money !== (int)$source_price`，免费源码直接拒绝走购买流程 | `dockingshop_money.php` |

### 7.2 Bug 修复（管理后台前端）

| # | 问题 | 原因分析 | 修复方案 |
|---|------|----------|----------|
| 4 | **概览"待审核源码"统计卡一直处于选中（高亮）状态**：即使待审核数为 0 仍显示陶土红高亮边框+红字 | 统计卡数据第三项（accent 高亮开关）写死为 `true`：`['待审核源码', s.audit, true]`，与其他卡片的动态判断（如 `s.report > 0`）不一致 | 改为 `s.audit > 0`：**有待审核时高亮提醒、处理完毕自动恢复正常样式**，与"待处理举报"卡片逻辑一致 |
| 5 | 详情弹窗售价显示"0 咕币" | 前端 `detailBody` 判免费用 `s.money == null`（宽松比较下 `0 == null` 为 false），后端 money 规范化为数字 0 后落入付费分支 | 售价/列表/编辑三处统一改为 `Number(money)` 数值判定（`!Number(s.money)` 即免费），同时兼容旧数据空串/`"0"` |

### 7.3 白昼/黑夜模式优化

| 项目 | 说明 |
|------|------|
| **原生控件适配** | 新增 `color-scheme: light / dark` 声明：深色模式下浏览器原生滚动条、表单控件、输入光标自动切换为深色系（此前深色页面配浅色滚动条） |
| **旧内核兼容（关键）** | 全部 9 处 `color-mix()`（顶栏毛玻璃/表头底色/行悬停/标签边框/提示条/登录装饰圆）增加同位置 `rgba()` 回退值 + `html.dark` 回退。**iOS 16.2 以下 / Chrome 111 以下不支持 color-mix 的 WebView 此前整条声明失效**，顶栏会变全透明、内容滚动时文字重叠——现在自动降级为半透明纯色，两主题均正常 |
| **登录页主题切换** | 登录卡片右上角新增主题切换按钮（此前登录页无法切换主题，只能登录后切换） |
| **跟随系统偏好** | 未手动设置过主题时，自动跟随系统 `prefers-color-scheme` 深浅偏好；手动切换后以用户选择为准（localStorage 持久化） |
| **深色调色微调** | 深色模式 `--sky` 提亮（#6A9BCC → #8FBCE0，待审核标签更易读）；弹窗遮罩深色下加深（0.45 → 0.62，聚焦更明确）；两处主题按钮图标状态实时同步 |

### 7.4 移动端适配优化（表格卡片化）

v2.1 采用"表格横向滚动"方案，v2.2 升级为**卡片化布局**，体验质变：

| 项目 | 说明 |
|------|------|
| **数据表格卡片化** | 手机端（≤640px）审核/源码/评论/用户四个六列大表格自动转为**纵向卡片**：每条记录一张卡，字段上方显示小标签（源码/作者/售价/提交时间/操作…），**彻底取消横向滚动**——此前需左右滑动才能看到的操作按钮（通过/驳回/下架/封禁…）现在直接完整可见可点 |
| **实现方式** | 表格渲染时为每个 `<td>` 输出 `data-label` 字段名，CSS 在移动端隐藏表头、将行/列转块级、`::before` 输出字段标签；桌面端（>640px）完全不受影响 |
| **概览待办表** | 三列紧凑结构不卡片化，移动端收紧内边距与按钮尺寸，一行放得下"事项/数量/去处理" |
| **保留能力** | v2.1 的全部移动端能力（16px 防缩放、安全区、统计卡 2 列、触屏禁悬停位移等）全部保留 |

### 7.5 验证方式与结果

- **逻辑等价验证（35 项断言全部通过）**：token 验证在 GET/表单/JSON（数字与字符串时间戳、带 charset 的 Content-Type）提交方式下行为一致；复现修复前 JSON 提交 403 现象；`timestamp:null` 明确报"缺少必要参数"；money 规范化覆盖空串/"0"/0/数字/字符串/负数/脏数据全部分支；前端免费判定与显示三处全兼容。
- **agent-browser 实测（桌面 1280px + 移动 390px + 系统深色模拟）**：
  - 概览高亮修复：audit=2 时卡片高亮 ✓；切换 audit=0 后卡片不再高亮（修复前恒高亮）✓；封禁用户数>0 时仍正确高亮 ✓
  - money 显示：付费源码"50 咕币"/"10 咕币"、免费源码"免费"、编辑弹窗免费源码售价留空 ✓
  - 深色模式：body/顶栏/表头/卡片计算样式全部命中深色值、color-scheme=dark、清除存储后跟随系统深色 ✓
  - 移动端卡片化：表头隐藏、行转块级、data-label 齐全、**无横向滚动**、操作按钮在视口内可见 ✓
  - 桌面回归：表格恢复 table 布局（min-width 600px）、弹窗必填原因校验（保持打开+聚焦）等 v2.1 行为零破坏 ✓
  - 全程无控制台报错；VLM 视觉审查确认卡片布局无重叠截断、深色配色协调。
- **静态检查**：25 个 PHP 文件语法检查、index.php 两个 script 块 Node 语法检查、CSS 花括号配对全部通过。

### 7.6 升级说明

| 场景 | 操作 |
|------|------|
| 已部署 v2.1 | 覆盖增量包中的 21 个文件即可：`token.php`、`dockingshop_common.php`、18 个 `dockingshop_*.php` 接口、`dockingshop/admin/index.php`（本次 `admin/api.php` 无改动） |
| 全新部署 | 直接使用"轻鸽后端-完整更新版.zip" |
| APP 前端 | 源码商城接口现可任选 GET / 表单 / JSON 方式提交（timestamp/sign 与业务参数同体传递即可）；`money` 字段统一为数字，**0 = 免费**，无需再判空串 |

---
*AI生成*
