# 思诺智慧物业｜商业地产物业运营管理系统 — 产品需求文档（PRD）

> 本文回答的是「这套系统应该做成什么样、为什么这么做」。
> 需求原文的逐条落地位置、阶段划分与预留说明，见 [`需求落地对照表.md`](需求落地对照表.md)；
> 路由与入参出参见 [`接口概要.md`](接口概要.md)；表结构见 [`数据库设计.md`](数据库设计.md)。
> 本文与代码同步维护：**文中写的每条口径都在代码里可找到对应实现**，找不到的会明确标成「二期」。

| 项目 | 内容 |
|---|---|
| 产品全称 | 思诺智慧物业｜商业地产物业运营管理系统 |
| 短名 | 商业地产物业 |
| 线上演示 | <https://p.zerocod.cn/commercial/> |
| 本期范围 | 第一阶段（基础闭环）+ 第二阶段中已实现的台账与报表 |
| 读者 | 客户方管理者与项目负责人、实施与运维人员、后续接手开发的工程师 |

---

## 一、产品定位

### 1.1 一句话

给**商业地产（购物中心 / 写字楼 / 商业街）的物业服务商**做的一套运营系统：把「商户与顾客的诉求、每天必须干的活、设备设施的维保、铺位商户的档案、超出免费范围的有偿服务、失物招领、甲方汇报」串成一条闭环，让每个动作都有人、有时间、有照片、有账可对。

### 1.2 目标客户与典型场景

| 客户类型 | 典型规模 | 最痛的事 |
|---|---|---|
| 商业地产物业服务商（在管 1~10 个项目） | 单项目 5~15 万㎡、商户 60~300 家、一线 30~80 人 | 诉求散在微信群与电话里，事后说不清；保洁工程「干了没人知道、没干也说不清」；甲方要数据时只能翻记录 |
| 商业地产自持物业团队 | 单一购物中心 | 物业与招商、运营之间数据不通，铺位/商户档案与工单对不上号 |
| 甲方（地产/资产方） | 委托物业服务商 | 想知道钱花在哪、活干成什么样，但不想进到执行细节 |

**系统要替代的现状**：微信群喊人 + 电话催办 + Excel 记账 + 纸质点检表。

### 1.3 使用者画像与诉求

| 角色 | 现场怎么用 | 最在意什么 |
|---|---|---|
| 物业总部运营总监 `hq` | 电脑上看总控看板、跨项目对账、SLA 口径配置 | 三个项目横向对比；哪条线掉链子 |
| 总部质控经理 `hq` | 抽查工单与作业留痕、看差评回访、质量分布 | 留痕真不真（有没有照片、有没有超时） |
| 总部成本专员 `hq` | 有偿服务台账、按商户月度对账、导出 CSV | 金额算得清、收得回来 |
| 项目经理 `pm` | 项目工作台、工单督办、派单、铺位商户档案 | 今天有哪些没办结、哪些超时了 |
| 班组主管 `lead` | 派单、验收、班组成员与考勤 | 派给谁了、干得合不合格 |
| 一线作业人员 `worker` | **只用手机**：接单、扫码到场、勾检查项、拍照、完工、报异常 | 步骤少、字大、不用打字 |
| 地产甲方 `owner` | 手机或电脑看项目看板与报表 | 只读、看得懂、不含内部人员细节 |
| 商户租户 `merchant` | 手机上报诉求、查进度、看自己的有偿账单 | 进度透明、账单明细清楚 |
| 装修施工方 `constructor` | 装修单与整改任务（**界面二期补齐**，本期已建号建表） | 整改要求明确、能留证 |

### 1.4 产品目标与成功判据

| 目标 | 判据（可验证） |
|---|---|
| 诉求不丢、过程可查 | 每条诉求都有单号、受理/派单/到场/完工/核验/评价六个里程碑，任一里程碑可查到人、时间与留痕 |
| 干过的活说得清 | 每条作业有执行人、到场打卡、检查项逐项结果、现场照片；主管验收结论可追 |
| 数字内外一致 | 看板卡片上的数字点进去，列表条数与卡片一致；报表口径与台账一致（自检脚本逐条比对） |
| 中老年员工能用 | 一线全流程在 `/m` 完成；每页右上角「使用说明」抽屉给到「这一步该点哪儿」 |
| 甲方要的能立刻给 | 甲方面板 + 报表 + CSV 导出，字段级脱敏，不需要后台操作 |

### 1.5 边界：明确不做

不做招商租赁合同与租金收缴（对接客户方租赁系统）、不做财务总账与税务开票、不做会员营销与客流分析、不做写字楼工位与会议室管理。系统聚焦**物业服务运营**本身。

---

## 二、术语与概念

统一叫法，避免项目上「工单/任务/派工单」混着叫导致统计口径打架：

| 术语 | 含义 | 数据落点 |
|---|---|---|
| 项目 | 一个在管物业（购物中心 / 写字楼 / 商业街） | `projects` |
| 楼栋 / 楼层 / 铺位 | 主数据三层；铺位是「可出租单元」，含空置与公区展位 | `buildings` / `floors` / `stores` |
| 商户 | 承租铺位的经营主体，也是有偿服务的付费方 | `merchants` |
| 诉求工单 | 来自顾客/商户/巡查/电话的一次请求，有 SLA 与闭环流程 | `tickets` |
| 作业 | 按模板周期排出的现场工作（保洁、巡检、保养…） | `tasks` |
| 检查项 | 一条作业里逐项勾选的标准动作（由作业标准拆出） | `task_checkpoints` |
| 点位 | 贴了二维码的现场位置；扫码即打卡定位 | `points` |
| 班组 / 业务线 | 作业的组织单位 / 五条专业线（保洁、工程、秩序安保、绿化、车场） | `teams` / `Dict::LINE` |
| 有偿服务 | 超出物业免费范围、由商户承担费用的维修与工程服务 | `paid_records` |
| 失物招领 | 拾获物品的登记、保管与认领台账 | `lost_found` |
| 商户纠纷 | 商户之间的经营摩擦（噪音、油烟、占道）调解记录 | `ticket_disputes` |
| 甲方 | 委托物业服务的地产/资产方，只读视角 | `users.role_code = owner` |
| SLA | 响应时限与办结时限的承诺，按事项类型 × 优先级配置 | `sla_rules` |
| 留痕 | 现场照片、检查项结果、流转轨迹、操作审计 | `media` / `*_logs` / `audit_logs` |
| 差评回访 | 评分 ≤ 3 的工单必须回访 | `tickets.rating_score` |

---

## 三、业务蓝图

```
                    ┌──────────────── 甲方（只读看板 / 报表 / CSV）────────────────┐
                    │                                                              │
主数据：项目 → 楼栋 → 楼层 → 铺位 → 商户 ──┬─→ 诉求工单闭环（顾客/商户/巡查/电话）─┴─→ 评价与回访
                                        ├─→ 日常作业闭环（模板 → 排程 → 接单 → 到场 → 检查项 → 完工 → 验收）
                                        ├─→ 设备设施闭环（台账 → 保养周期 → 到期 → 故障转单 → 年检）
                                        └─→ 台账（有偿服务 / 失物招领 / 商户纠纷）
治理：待办 · 消息 · 审计 · 文档 · 帮助（贯穿四条线）
```

### 3.1 诉求工单闭环

`上报（扫码 / 表单 / 客服代录 / 巡场发现 / 电话）→ 自动归口分类与定级 → 按 SLA 计时 → 受理 → 派单 → 到场处理 → 完工提交 → 主管核验 → 顾客评价 → 归档`，全过程落 `ticket_logs`。

关键取舍：
- **L1 客服台不必存在**：上报后自动按事项类型与业务线选定执行人（`assign_type = auto`），主管可改派。小项目没有专职客服，多一层人工派单反而拖响应。
- **核验与评价分开**：主管核验是「活干得对不对」，顾客评价是「满不满意」。两者都留痕，差评只看后者。
- **公区单不落铺位**：走廊异味、外立面清洗这类活不该挂到某个商户名下（见 6.7）。

### 3.2 日常作业闭环

`作业模板（日/周/月/一次性 + 业务线 + 标准）→ 系统按日排程生成作业 → 一线在移动端接单 → 扫码到场打卡 → 逐项勾选检查项 + 拍照 → 完工提交 → 主管验收（合格 / 不合格退回整改）→ 计入班组工作量与考勤`。

关键取舍：
- **检查项不另写一份**：作业标准本来就是「一条条要检查的内容」，按顿号拆开即检查项（见 `task_checkpoints`），避免模板维护两套清单。
- **异常可转工单**：现场发现「不是自己能修的」问题，一键转成诉求工单并自动带上位置与照片，不要求一线判断归口。

### 3.3 设备设施闭环

`设备台账（电梯/空调/配电/消防/给排水/其他）→ 维保周期与下次保养日 → 到期提醒 → 自动排出保养作业 → 故障时转报修工单（挂该设备与安装位置）→ 年检跟进`。

关键取舍：设备保养不另做一套工单类型，而是生成**作业**（`tasks.device_id` 指回设备）；故障才转**工单**。这样「计划性工作」与「突发诉求」在各自的看板上统计，不会互相污染完成率。

### 3.4 主数据闭环

项目 → 楼栋 → 楼层 → 铺位 → 商户五级；工单挂铺位与商户，账单挂商户与铺位，作业挂点位与班组。四组关系在两处强制校验（见 8.2）：铺位「已出租」必须有商户、商户档案的铺位清单与铺位表的商户字段必须双向一致。

### 3.5 台账（有偿服务 / 失物招领 / 商户纠纷）

- **有偿服务**：工单办结后登记工时与计费明细（材料费 / 机械使用费），系统拆出「材料费 + 机械使用费 + 工时费」三类与小时单价，按商户月度对账（`/paid`、`/paid/{id}`）。一期只到「据实登记 + 明细可查 + 累计对账」，审批与结算流在二期（见 9.2）。
- **失物招领**：拾获登记 → 保管中 → 认领登记（必须留认领人与电话 + 凭证照片）或过期处理（`/lost`、`/lost/{id}`）。
- **商户纠纷**：纠纷双方、原因、调解结果与调解人（挂工单，`ticket_disputes`）。

### 3.6 汇报线

甲方只读面板（`/dash/project/{id}`）+ 报表（`/reports/*`）+ CSV 导出（`/export/*`），字段级脱敏（内部人员工作量、内部备注不外露）。

### 3.7 治理

待办（谁该干什么）、消息（推给谁）、审计（谁动过什么）、文档中心（`/docs`，就地看交付文档）、帮助库（每页使用说明抽屉）。

---

## 四、角色与权限

### 4.1 角色矩阵

| 角色 | 可见数据范围 | 可写动作 | 典型入口 |
|---|---|---|---|
| 总部管理员 `hq` | 全域全部项目 | 全部（含 SLA / 字典 / 系统参数） | `/dash`、`/reports/workload`、`/sys/*` |
| 项目经理 `pm` | 仅本项目 | 本项目工单/作业/主数据/有偿服务 | 项目工作台、`/tickets`、`/stores` |
| 班组主管 `lead` | 本项目本业务线 | 派单、验收、整改、本项目本线有偿服务登记 | `/tasks`、`/tickets`、`/attendance` |
| 一线人员 `worker` | 仅本人任务与本人单据 | 接单、打卡、勾检查项、拍照、完工、报异常 | `/m/*` |
| 甲方 `owner` | 本项目（只读） | 无（字段级脱敏） | `/dash/project/{id}`、`/reports/*` |
| 商户 `merchant` | 仅本商户（铺位、工单、账单） | 上报诉求、评价 | `/m`、`/m/tickets`、`/report` |
| 施工方 `constructor` | 仅本人单据（二期界面） | 装修单与整改任务（二期） | 二期 |

### 4.2 数据隔离的实现口径

- 所有列表/详情查询统一走 `App\Services\Scope`，禁止在控制器里手写 `WHERE project_id = ...`——一处漏写就是一次越权。
- 外部账号（甲方/商户/施工方）为**只读 + 受限写**，`Controller::guardWritable()` 拦住越界写入。
- 公区历史数据（未租出铺位的记录）对商户不可见：避免把空铺的历史工单泄露给同层商户。
- 越权一律 `403` + 明确提示页，不返回 500、不返回空列表（空列表会让客户以为「本来就没数据」）。

### 4.3 字段级脱敏

甲方视角隐藏内部人员的工作量与内部备注；商户视角只暴露自己商户的金额与进度，不暴露其他商户与公区记录。

---

## 五、功能需求

编号规则：`FR-模块-序号`。每条都给出页面入口与**可验证的验收标准**（自检脚本能自动判定的会注明脚本编号）。

### FR-1 主数据

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-1-1 | 项目档案 | 业态（购物中心/写字楼/商业街/产业园）、建筑面积、管理面积、铺位总数 | `/projects`、`/projects/{id}` | 项目详情可下钻到楼栋、铺位、工单、作业、设备；`store_total` 与铺位数一致（C44） |
| FR-1-2 | 楼栋与楼层 | 楼层必须属于本项目楼栋 | `/buildings` | 楼栋与楼层不得跨项目（C39/C40） |
| FR-1-3 | 铺位档案 | 铺位号、面积、业态、状态（空置/已租赁/待撤场）、是否公区 | `/stores`、`/stores/{id}` | 「已出租必须有商户、空置必须无商户」（C2x）；铺位档案页一屏看全在办工单/履历/铺内作业/同层铺位/楼层设备 |
| FR-1-4 | 商户档案 | 经营主体、联系人、业态与行业、合同起止、账号开通 | `/merchants`、`/merchants/{id}` | 合同结束日不得早于起始日（C45）；商户所属项目与铺位一致（C46） |
| FR-1-5 | 五级下钻 | 项目 → 楼栋 → 铺位 → 工单/账单/作业 | 各档案页 | 每个数字都能点回带筛选的台账，且筛选条件在列表页回显为「筛选条」 |

### FR-2 诉求工单

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-2-1 | 多渠道上报 | 扫码 / 线上表单 / 电话代录 / 巡场发现；来源标记区分顾客上报、作业异常、质控检查、装修巡检、突发事件 | `/report`、`/scan/{token}`、`/tickets/new`、`/tickets` | 免登录上报入口可用；提交后给出单号与查询方式 |
| FR-2-2 | 自动归口与定级 | 按事项类型映射业务线，按事项类型 × 优先级取 SLA | `/tickets/new` | 建单页实时试算响应/办结时限与升级人（读 `sla_rules`） |
| FR-2-3 | 全流程流转 | 待受理 → 已受理 → 已派单 → 处理中 → 待核验 → 已办结 → 已关闭（可作废） | `/tickets/{id}` | 非法流转被服务端拦下并给出明确提示（见 6.1） |
| FR-2-4 | 超时督办 | 响应/办结超时实时判定，可按级别升级到督办人 | `/tickets?timeout=1`、`/tickets/scan` | 卡片数字与列表条数一致（自检 `tmp` 类脚本 + `check.php` C5/C5b） |
| FR-2-5 | 评价与回访 | 归档时顾客评分（1~5）；≤3 分需回访 | `/tickets?low_rating=1` | 存在差评样本（C2x 分布）；差评可筛可下钻 |
| FR-2-6 | 诉求报表 | 按业务线、事项类型、来源、状态、客户类型分布 | `/tickets/report` | 分布比例不得一家独大（D 组检查） |
| FR-2-7 | 凭单号查询 | 免登录查进度：单号 + 手机尾号双因子 | `/track`、`/track/result` | 尾号错误必须查不到（冒烟已断言） |
| FR-2-8 | 工单转作业 | 需要现场施工的工单可转作业并回填结果 | `/tickets/{id}` | 转派后作业与工单双向可见 |

### FR-3 日常作业

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-3-1 | 作业模板 | 业务线 + 周期（日/周/月/一次性）+ 标准 + 执行人/班组；检查项由标准自动拆分 | `/templates` | 模板引用的班组与人员必须存在（C38） |
| FR-3-2 | 自动排程 | 按周期生成当日作业，落在项目营业时段内 | `/tasks` | 近 30 天不得有断档日（E 组） |
| FR-3-3 | 移动端执行 | 接单 → 到场打卡 → 检查项 → 拍照 → 完工 → 异常 | `/m/tasks`、`/m/task/{id}` | 移动端详情能渲染出作业号（冒烟已断言） |
| FR-3-4 | 验收与整改 | 主管验收合格/不合格；不合格退回整改并推待办 | `/tasks/{id}` | 验收结果与状态自洽；重复完工被拦（见 6.2） |
| FR-3-5 | 考勤 | 打卡与班次联动（迟到/早退/缺勤/请假/加班） | `/attendance`、`/m/checkin` | 下班打卡不得早于上班；工时区间合理（C 组） |
| FR-3-6 | 工作量对账 | 按人/班组统计完成量与被验收情况 | `/reports/workload` | 与台账一致；可导出 CSV |
| FR-3-7 | 扫码点位 | 点位二维码（含令牌），扫即定位 | `/points`、`/scan/{token}` | 令牌唯一且齐全；启用点位近 30 天必须有作业（E11） |

### FR-4 设备设施

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-4-1 | 设备台账 | 类型、安装位置、编号、状态（正常/维保中/故障/停用）、保养周期与下次保养日 | `/devices`、`/devices/{id}` | 保养周期与下次保养日一致（C 组） |
| FR-4-2 | 保养计划 | 按周期生成保养作业（`tasks.device_id`） | `/tasks`、`/devices/{id}` | 设备档案「关联维保作业」非空 |
| FR-4-3 | 故障转单 | 设备故障转报修工单，位置取安装位置、标题含设备名 | `/devices/{id}` | 故障设备当天有在办检修；工单挂该设备（C 组） |
| FR-4-4 | 年检跟进 | 年检日期与到期提醒 | `/devices` | 到期设备在列表可见并可筛选 |

### FR-5 台账

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-5-1 | 有偿服务登记 | 仅已办结/已关闭工单、必须有承担费用的商户、一单一条 | `/paid` | 服务端校验（见 6.5）；脏数据检查 C55 系列 |
| FR-5-2 | 账单明细 | 按类别（材料费 / 机械使用费）逐项列明、拆分三类金额与工时费、折算小时单价、列同一商户历史与累计 | `/paid/{id}` | 类别取 `pay_item` 字典；明细小计 > 合计时给出对账提示；留痕可查 |
| FR-5-3 | 失物登记与认领 | 保管中 → 已认领 / 已过期处理；认领必须留认领人 + 电话 | `/lost`、`/lost/{id}` | 状态取值必须在字典内；已认领缺认领人必须被拦（C54） |
| FR-5-4 | 失物档案 | 拾获位置、时间、编号、关联工单、凭证照片、处置轨迹、同项目仍在保管中的失物 | `/lost/{id}` | 页面渲染出 LF 编号（冒烟已断言） |
| FR-5-5 | 商户纠纷 | 纠纷双方铺位、原因、调解结果与调解人 | 工单详情 | 纠纷记录必须挂工单与项目 |
| FR-5-6 | 导出台账 | 工单/作业/工作量/铺位/商户/设备/考勤 | `/export/*` | 内容必须是有效 CSV；无权限角色返回 403 而非 500 |

### FR-6 报表与甲方汇报

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-6-1 | 项目看板 | 今日受理/办结/在办/超时、业务线分布、铺位 TOP、趋势 | `/dash`、`/dash/project/{id}` | 卡片数字 = 下钻列表条数（端到端核对） |
| FR-6-2 | 工单达标率报表 | 按项目/时间统计响应与办结达标率 | `/reports/tickets` | 与 SLA 判定同源 |
| FR-6-3 | 作业完成率报表 | 按业务线/班组统计完成与验收情况 | `/reports/tasks` | 与作业台账同源 |
| FR-6-4 | 工作量对账 | 人员/班组工作量与验收率 | `/reports/workload` | 支持项目筛选与导出 |
| FR-6-5 | 甲方只读视图 | 本项目数据、字段级脱敏、无内部操作按钮 | `/dash/project/{id}`、`/reports/*` | 甲方账号看不到内部人员工作量与内部备注 |

### FR-7 移动端

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-7-1 | 移动作业台 | 今日任务、待办、消息 | `/m` | 一线账号可用；无现场作业的角色自动分流 |
| FR-7-2 | 作业执行 | 接单、到场打卡、检查项、拍照、完工、异常 | `/m/task/{id}` | 全流程可脱离电脑完成 |
| FR-7-3 | 我的单据 | 我的诉求、进度、账单（商户） | `/m/tickets`、`/m/ticket/{id}` | 商户只能看到自己的单据 |
| FR-7-4 | 离线补传 | 弱网下本地队列，恢复后补传 | `/m/sync` | CSRF 豁免按应用内路径判定（`index.php?r=` 形态下同样生效） |

### FR-8 对外入口（免登录）

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-8-1 | 扫码上报 | 点位令牌定位，可匿名提交 | `/scan/{token}`、`/report` | 令牌无效给出明确提示 |
| FR-8-2 | 进度查询 | 单号 + 手机尾号双因子 | `/track`、`/track/result` | 双因子错误必须查不到 |
| FR-8-3 | 服务介绍 | 受理范围与时限承诺**读自 `sla_rules`**，不写死文案 | `/intro` | 页面承诺的分钟数与工单实际计时一致 |
| FR-8-4 | 感谢页 | 提交后落地页 | `/thanks/{code}` | 可正常渲染 |

### FR-9 组织与系统配置

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-9-1 | 账号与角色 | 七类角色；账号必须有所属项目（商户/施工方） | `/org`、`/users`、`/roles` | 商户账号必须有项目（C47）；演示账号必须有密码（C48） |
| FR-9-2 | 班组与业务线 | 一线人员必须有班组与业务线 | `/org` | C49 警告；派单时不会选不到人 |
| FR-9-3 | SLA 规则 | 总部默认 + 项目/业务线更严口径；规则取值必须在字典内 | `/sys/sla` | C53 系列；矩阵必须覆盖库里出现的「事项类型 × 优先级」 |
| FR-9-4 | 字典维护 | 可配置字典（业态、行业、事项类型等） | `/sys/dicts` | 页面上不得出现英文枚举（`content.php`） |
| FR-9-5 | 系统参数 | 项目参数、业务参数 | `/sys` | 参数变更需留审计 |
| FR-9-6 | 界面偏好 | 字号、密度、主题（照顾中老年员工） | `/sys/ui`、`/panel/prefs` | 偏好随账号保存 |

### FR-10 治理

| 编号 | 功能 | 关键规则 | 入口 | 验收标准 |
|---|---|---|---|---|
| FR-10-1 | 待办 | 谁该干什么；与业务对象状态同步收敛（见 6.9） | `/todos` | 不得出现「点进去事情早办完了」的僵尸待办 |
| FR-10-2 | 消息 | 派工、待核验、整改、超时升级推给对应角色 | `/panel/messages`、`/messages` | 演示库必须有未读消息（E 组） |
| FR-10-3 | 通讯录与人员档案 | 点人名进人员档案（项目/班组/主管/同事/名下作业与经手工单） | `/contacts`、`/contacts/{id}` | 所有登录角色可达；商户/甲方只看联系方式 |
| FR-10-4 | 审计日志 | 谁在什么时候改了什么；业务对象可直接点进详情 | `/audit` | 审计条目的目标可点（含 `paid_record`、`lost_found`） |
| FR-10-5 | 文档中心 | 就地读取 `docs/` 下的交付文档 | `/docs` | 线上可见（`docs/` 随部署上传） |
| FR-10-6 | 帮助库 | 每页右上角使用说明抽屉 | `/help` | 每个主要页面都有说明 |

---

## 六、业务规则与统一口径

这一节是「系统为什么这么算」的单一来源。**口径冲突时以本节为准**，并回头改代码与页面文案。

### 6.1 工单状态机与流转校验

```
created 待受理 → accepted 已受理 → assigned 已派单 → processing 处理中 → wait_check 待核验
      → finished 已办结 → closed 已关闭            （canceled 已取消，可在办结前作废）
```

- 流转动作全部在 `Services\Ticket` 里做**服务端**校验，前端只负责隐藏按钮；构造表单直接提交同样会被拦下并提示原因。
- 典型误操作拦截：跳过派单直接核验办结、已办结再升级、未受理却写处理人。

### 6.2 作业状态机与验收

```
wait 待执行 → accepted 已接单 → doing 执行中 → finish 已完成 → 已验收
                        ↘ abnormal 异常 → 现场处置 或 转工单
```

- 重复完工被拦（否则会刷出多条完工流水）；已验收作业不能再打卡；已办结作业不能作废。
- 验收不合格 → 退回整改，并给执行人推「整改」待办。

### 6.3 SLA 计算与超时升级

- 匹配顺序：**项目专属 > 总部默认**，且「业务线 + 事项类型 + 优先级」越具体越优先（`Ticket::sla()`）。
- 命中不到专属规则时回落到**按优先级的粗口径**：紧急 10 分钟响应 / 120 分钟办结；高优 15/240；普通 30/480。这是兜底，不是推荐配置。
- 默认矩阵（总部）覆盖「事项类型 × 优先级」全组合：报修与投诉三档齐全；建议只到普通与高优；失物招领只到普通；商户纠纷三档齐全。项目级再叠加更严的口径（详见 `/sys/sla` 与附录 C）。
- `escalate_min` 的默认口径：**高优与紧急等于其响应时限**（到点还没响应就升级给项目经理），普通单为 0（不额外打扰主管）。
- `is_sla_timeout` 一律由「实际完成时间（未完成则按此刻）vs 办结时限」算出，不用随机数、不等于标记列的快照：**标记列是建单时的快照，工作台与台账按当前时间实时判定**，两者差异由 `check.php` C5b 说明。

### 6.4 评价与差评回访

- 评分 1~5 分，只在归档（`closed`）时产生。
- **≤ 3 分视为差评**，必须回访；`/tickets?low_rating=1` 可筛出全部差评。
- 演示数据口径：超时单以 2~3 星为主并有少量 1 星；达标单以 5 星为主。差评不是随机撒的，而是与超时结果挂钩——否则「超时很多但全是五星」一眼假。

### 6.5 有偿服务计费与对账口径

- **前提**：事项为报修、工单已办结/已关闭、有承担费用的商户（公区单不可登记），且一张工单只能有一条账单。四条都在服务端强制。
- **拆分**：`合计金额 = 材料费 + 机械使用费 + 工时费`，按 `pay_item` 字典分类（`material` 材料费 / `machine` 机械使用费，如高空作业车台班、疏通机、吸污车），明细逐项列明（数量 × 单价），工时费 = 合计 − 材料费 − 机械使用费，并折算小时单价。
- **异常提示**：明细小计（材料 + 机械）> 合计金额时，账单页明确标红（常见于改了明细没改金额），而不是把它算成「负数工时费」悄悄带过。
- **对账**：账单页并排列出同一商户的历史记录与累计金额，回答商户月底的三个问题——修了什么（关联工单与完工说明）、钱怎么算的（计费明细）、这个月一共多少（累计）。
- 一期**不做**审批与结算流（见 9.2）。

### 6.6 失物保管与处置口径

- 状态三档：`keeping 保管中` / `claimed 已认领` / `expired 已过期处理`，取值必须在字典内。
- 登记为「已认领」时，**认领人与联系电话必填**，并留存认领凭证照片；认领时间只写一次（重复认领不改写时间）。
- 保管中的记录不得带认领时间。

### 6.7 公区 / 铺位归属规则

- 公区工单：不落铺位、不挂商户（避免「公共区域异味」挂到某个商户名下）。
- 铺位工单：位置必须带该铺位号；铺位「已出租」必须有商户，「空置」不得挂商户。
- 作业位置必须是台账里的点位名称；作业标题里的楼栋与点位必须同栋（否则移动端扫码定位对不上）。

### 6.8 时间口径

- 演示/生产数据一律**不得出现未来时间**；工单六个里程碑严格递增且不越过此刻。
- 诉求发生时刻落在项目营业时段内：购物中心 09:00–22:00、写字楼 08:00–19:00、商业街 11:00–23:00。早 8 点的「排烟噪音投诉」、打烊后的顾客在逛街，都是不合逻辑的数据。
- 业务量按业态与周末错峰：购物中心周末更高、写字楼工作日更高，避免三个项目数字一模一样。

### 6.9 待办收敛

一个动作会关闭哪些待办是有明确规则的，否则会出现僵尸待办：

| 触发动作 | 关闭 | 新推 |
|---|---|---|
| 一线完工提交 | 该作业的派工提醒 | 给主管推「待核验」 |
| 主管验收合格 | 该作业的核验提醒 | —— |
| 验收不合格退回 | —— | 给执行人推「整改」 |
| 工单办结 / 归档 / 评价 | 该工单所有未办提醒 | —— |
| 设备保养到期 | —— | 给工程线推「保养」 |
| 作业转工单 | 该作业的处置提醒 | 给对应业务线推「派单」 |

### 6.10 留痕与审计口径

- 现场留痕：工单上报/完工、作业检查项、失物认领凭证均需照片；库里每条媒体地址都必须能在 `public/` 下找到文件（部署漏图会让详情页出现空白图框）。
- 操作留痕：状态流转落 `ticket_logs` / `task_logs`；跨模块动作落 `audit_logs`，且审计条目的目标对象可点击直达详情。

---

## 七、非功能需求

### 7.1 性能与容量

| 项 | 目标 | 现状 |
|---|---|---|
| 数据规模 | 单项目 5 年工单 ≤ 20 万条、作业 ≤ 50 万条 | SQLite 单文件库可承载；已附 MySQL 8.0 DDL 供迁移 |
| 列表页 | 首屏 ≤ 1.5s（本地 cli-server，千级数据） | 台账列表默认 200 条上限 + 分页 |
| 看板 | 卡片数字由聚合查询直接给出，不在 PHP 里遍历全表 | `Services\Stats` 全部走 SQL 聚合 |
| 导出 | 7 类台账可导出 CSV，十万级行不撑爆内存 | 逐行流式输出 |

### 7.2 可用性与易读性

- **每页都有使用说明**（右上角抽屉）：说明「这一页看什么、这一步点哪儿」，面向不熟悉系统的中老年员工。
- 空状态不空着：列表无数据时说明**为什么可能没有**、下一步该点哪里。
- 枚举一律翻译成中文，不得把 `wait_check`、`emergency` 这类 code 铺到界面上（`content.php` 专门抓这个）。
- 数字要能被追问：看板与报表的每个数字都能点进带筛选的列表，且筛选条件在页面顶部回显为筛选条。

### 7.3 移动优先与离线

一线全流程可在手机完成（`/m`）；弱网环境下本地队列缓存，恢复后补传（`/m/sync`，按应用内路径豁免 CSRF）。

### 7.4 安全

- 登录：口令哈希存储；演示账号口令统一为 `123456`（仅演示环境）。
- CSRF：所有 POST 统一校验令牌；`/m/sync` 按应用内路径豁免（伪静态与 `index.php?r=` 两种形态都判对）。
- 越权：`Scope` 统一拦截 + `403` 统一页；上报/查询免登录入口只暴露必要字段，查询需双因子。
- 目录保护：`app|config|database|routes|scripts` 三层拦截（`router.php` 守卫、脚本内置守卫、`.htaccess`），防止 `database/seed.php` 被访客直接触发重建。
- 会话隔离：`session_name = sino_commercial_session`，与同域其它系统不串号。
- 库文件位置：落在 Web 根之外（系统临时目录），不可被当静态文件下载。
- 开放重定向：`back` 之类来自表单的跳转目标只接受本模块内部路径。

### 7.5 兼容与部署

- PHP 8.0+（已在 8.3 验证）、`pdo_sqlite`；无需构建步骤（原生 CSS/JS）。
- 兼容两种寻址形态：伪静态与 `index.php?r=`（线上 openresty 无重写规则，`url()` 自动降级）。
- 部署：FTP 增量同步 + 已传清单（远端列表被截断时仍能正确判断），`docs/` 一起上传以便站内文档中心可用。
- 首次访问自动建表并灌演示数据（`demo_mode`）。

### 7.6 可配置

SLA 规则、作业模板、字典、系统参数、界面偏好均可在后台改，不改代码。

### 7.7 自检与回归

四套脚本构成回归基线（详见 README 第七节）：

| 脚本 | 抓什么 | 本期结论 |
|---|---|---|
| `scripts/smoke.php` | 全页面 200 / 无 500 / 详情页渲染出真实业务主键 / 筛选不为空表 / 导出是有效 CSV / 越权必须是 403；支持六个角色分别跑 | 全部通过 |
| `scripts/check.php` | 结构、外键、状态与时间戳自洽、SLA、分布比例、30 天连贯性、跨模块关联（含本轮新增 C53/C54/C55 系列） | 0 错误 0 警告 |
| `scripts/links.php` | 路由 → 控制器方法 → 视图文件 → 代码内链接 → 导航登记；以及下钻参数有没有人读 | 全部通过 |
| `scripts/content.php` | 字典泄漏、脏值（1970-01-01 / null / Array / NaN）、整页空表 | 未发现问题 |

---

## 八、数据要求

### 8.1 表结构概览（29 张）

| 分组 | 表 |
|---|---|
| 主数据 | `projects` `buildings` `floors` `stores` `merchants` |
| 组织与权限 | `roles` `users` `user_project_rel` `user_prefs` `teams` |
| 诉求工单 | `tickets` `ticket_logs` `paid_records` `lost_found` `ticket_disputes` |
| 日常作业 | `task_templates` `tasks` `task_logs` `task_checkpoints` `attendance` `points` |
| 设备设施 | `devices` |
| 系统与治理 | `settings` `dicts` `audit_logs` `todos` `messages` `media` `sla_rules` |

### 8.2 必须互相印证的关联（自检逐条覆盖）

| 关联 | 约束 |
|---|---|
| 铺位 ↔ 商户 | 「已出租」必有商户、「空置」必无商户；商户档案铺位清单与铺位表商户字段双向一致；不得跨项目 |
| 工单 ↔ 铺位/商户 | 公区单不挂商户；铺位单位置必须带铺位号 |
| 作业 ↔ 班组/点位 | 执行人所属班组与作业班组一致；位置必须是台账点位名；标题楼栋与点位同栋 |
| 工单 ↔ 作业 ↔ 设备 | 异常作业转工单带回原位置；故障工单挂该设备与其安装位置 |
| 有偿服务 ↔ 工单/商户 | 工单必须已办结、必须有商户、一单一条、登记时间不早于建单时间 |
| 失物 ↔ 状态 | 已认领必须有人与电话；未认领不得有认领时间；状态取值在字典内 |
| 时间轴 | 六个里程碑递增且不越过此刻；考勤下班不早于上班；不得有未来数据 |
| 新媒体引用 | 库中每条媒体地址都能在 `public/` 下找到文件 |

### 8.3 演示数据的生成口径

- 锚点是**执行时刻**：近 30 天 + 今天，今天的状态按当时的真实时钟生成（凌晨重建时今天的作业处于待接单，白天重建则晨间已完工、午后在做、晚间待开工）。
- 确定性：不用外部随机源，同一份种子每次重建结果一致（否则自检按主键取样本会对不上）。
- 业务量按项目体量与业态错峰（购物中心周末更高、写字楼工作日更高），量级刻意做出差异。
- 内容不千人一面：诉求标题、处理说明、验收意见、完工备注、异常描述按类型分池轮换（每池 4~12 句），相邻两条必不相同。
- 现场照片由 `seed.php` 用 GD 生成并落到 `public/uploads/`，与库中的媒体地址一一对应。
- 线上演示环境按「跨天 / 超过保鲜时长 / 种子内容版本变化」自动重灌，保证访客看到的永远是「几小时内正在运营」的现场。

---

## 九、分期规划

### 9.1 一期（已实现）

主数据（项目/楼栋/楼层/铺位/商户）、诉求工单闭环、日常作业运维、设备设施点检、用户与权限、移动端、基础看板与报表、对外上报与查询入口、有偿服务与失物台账、组织与系统配置、待办/消息/审计/文档/帮助。

### 9.2 二期（待办，按优先级）

| 优先级 | 事项 | 说明 |
|---|---|---|
| 高 | 装修施工方界面 | 已建角色、账号与数据关系，缺「装修单申请—巡检—整改—复验」的页面与流转 |
| 高 | 有偿服务审批与结算 | 一期到「登记 + 对账」；二期补审批人、结算批次、与财务口径对接 |
| 中 | 质控自查计划 | 按周期的自查任务与抽查比例（本期已有质控检查来源标记与审计） |
| 中 | 跨项目标准下发 | 总部模板/字典下发到项目、项目级差异回收 |
| 中 | 消息推送渠道 | 现为站内消息；补企业微信/短信外发 |
| 低 | 数据大屏 | 甲方接待场景的投放视图 |

### 9.3 三期（方向）

多项目标准化复制、跨项目调度与成本分析、全域对账、与客户方租赁/财务系统对接。

---

## 十、验收

### 10.1 验收方式

1. **自动**：四套自检脚本（smoke / check / links / content）全跑，本地与线上各一遍。
2. **人工走查**：按 10.2 的清单，用演示账号逐个角色过一遍关键路径。
3. **数据抽检**：随机抽 5 条工单 / 3 条作业 / 2 条账单 / 2 条失物，核对状态、时间、金额、照片是否自洽。

### 10.2 一期验收清单

| 项 | 判据 |
|---|---|
| 诉求全流程 | 从 `/report` 上报到 `/track` 查进度、到主管核验、到顾客评价，六个里程碑齐全且留痕可查 |
| 作业全流程 | 一线在 `/m` 完成接单→打卡→检查项→拍照→完工；主管验收与退回整改可用 |
| 设备闭环 | 设备档案能看到保养作业与故障工单履历 |
| 主数据一致 | 铺位与商户关系双向一致；铺位档案可下钻到全部相关单据 |
| 台账可对账 | 账单明细可拆解、可查留痕、可按商户累计 |
| 看板可信 | 卡片数字与下钻列表条数一致（至少核对 6 张卡片） |
| 权限与隔离 | 六个角色分别登录，越权访问返回 403 且提示明确 |
| 甲方视角 | 脱敏生效，无内部操作按钮 |
| 移动作业 | 一线全流程不碰电脑 |
| 自检 | `smoke` / `check` / `links` / `content` 全部通过 |

### 10.3 已知取舍与风险

| 项 | 取舍 / 风险 | 应对 |
|---|---|---|
| 数据库 | 演示用 SQLite；高并发写入不适合生产 | 已提供 MySQL 8.0 DDL；生产切换只需改连接配置并迁移数据 |
| 自动派单 | 按业务线自动选人，不做到「按技能/位置/负载」最优派单 | 中小项目够用；复杂派单规则二期做 |
| 有偿服务 | 无审批与结算流 | 二期补齐；期间用「账单详情 + 审计留痕 + 月底对账」兜住 |
| 演示数据 | 时间锚定生成时刻，跨天会「过期」 | 线上开启自动续期；演示前重跑 `php database/seed.php` |
| 离线补传 | 仅覆盖移动端作业执行场景 | 冲突处理为「以服务端状态为准」，不做双向合并 |
| 字典泄漏类问题 | 依赖 `content.php` 扫描而非类型系统强制 | 已在服务端对关键枚举（失物状态、SLA 取值）做校验，其余靠自检回归 |

---

## 附录 A 页面地图

| 分组 | 页面 |
|---|---|
| 工作台 | `/`、`/todos`、`/contacts`、`/contacts/{id}`、`/audit`、`/help`、`/docs`、`/messages`、`/panel/*` |
| 看板 | `/dash`、`/dash/data`、`/dash/project/{id}` |
| 工单 | `/tickets`、`/tickets/board`、`/tickets/report`、`/tickets/new`、`/tickets/{id}`、`/tickets/scan` |
| 台账 | `/paid`、`/paid/{id}`、`/lost`、`/lost/{id}` |
| 作业 | `/tasks`、`/tasks/board`、`/tasks/new`、`/tasks/{id}`、`/templates`、`/points`、`/attendance` |
| 设备 | `/devices`、`/devices/{id}` |
| 主数据 | `/projects`、`/projects/{id}`、`/buildings`、`/stores`、`/stores/{id}`、`/merchants`、`/merchants/{id}` |
| 组织与系统 | `/org`、`/users`、`/users/{id}`、`/roles`、`/sys`、`/sys/dicts`、`/sys/sla`、`/sys/ui` |
| 报表 | `/reports`、`/reports/tickets`、`/reports/tasks`、`/reports/workload`、`/export/*` |
| 移动端 | `/m`、`/m/tasks`、`/m/task/{id}`、`/m/tickets`、`/m/ticket/{id}`、`/m/checkin`、`/m/me` |
| 对外 | `/report`、`/track`、`/track/result`、`/intro`、`/scan/{token}`、`/thanks/{code}` |

## 附录 B 字典与枚举（页面上必须显示中文）

| 字典 | 取值 |
|---|---|
| 业务线 `LINE` | 保洁 `clean`、工程 `eng`、秩序安保 `order`、绿化 `green`、车场 `parking` |
| 项目业态 `PROJECT_TYPE` | 购物中心 `mall`、写字楼 `office`、产业园 `park`、商业街 `street` |
| 铺位状态 `STORE_STATUS` | 空置 `0`、已租赁 `1`、待撤场 `2` |
| 商户状态 `MERCHANT_STATUS` | 正常营业 `1`、已撤场 `0`、待入驻 `2` |
| 工单状态 `TICKET_STATUS` | 已提交/已受理/已派单/处理中/待核验/已办结/已关闭/已取消 |
| 事项类型 `ORDER_TYPE` | 报修 `repair`、投诉 `complain`、建议 `suggest`、失物招领 `lost_found`、商户纠纷 `merchant_dispute` |
| 优先级 `PRIORITY` | 普通 `low`、高优 `high`、紧急 `emergency` |
| 上报渠道 `ORDER_SOURCE` | 微信小程序 `wx`、扫码上报 `scan`、线上表单 `form`、电话登记 `phone`、巡查发现 `patrol` |
| 来源标记 `ORDER_SOURCE_TYPE` | 顾客上报、作业异常、质控检查、装修巡检、突发事件、电话登记 |
| 客户类型 `CUSTOMER_TYPE` | 顾客 `customer`、商户 `merchant`、企业办公 `enterprise` |
| 作业状态 `TASK_STATUS` | 待执行/已接单/执行中/已完成/异常/已作废 |
| 作业类型 `TASK_TYPE` | 周期作业、临时作业、工单转派、整改复检 |
| 周期 `CYCLE_TYPE` | 每日 `day`、每周 `week`、每月 `month`、一次性 `once` |
| 验收结果 `VERIFY_RESULT` | 合格 `pass`、不合格 `fail` |
| 设备类型 `DEVICE_TYPE` | 电梯、中央空调、配电、消防、给排水、其他 |
| 设备状态 `DEVICE_STATUS` | 正常 `normal`、维保中 `maintain`、故障 `fault`、停用 `stopped` |
| 考勤 `ATTENDANCE_STATUS` | 正常、迟到、早退、缺勤、请假、加班 |
| 失物状态 `LOST_STATUS` | 保管中 `keeping`、已认领 `claimed`、已过期处理 `expired` |

## 附录 C 默认 SLA 矩阵（总部口径，项目级更严的口径覆盖之）

| 事项类型 | 紧急（响应/办结） | 高优（响应/办结） | 普通（响应/办结） |
|---|---|---|---|
| 报修 | 10 / 120 分钟（升级 10） | 15 / 240 分钟（升级 15） | 30 / 480 分钟 |
| 投诉 | 10 / 120 分钟（升级 10） | 15 / 240 分钟（升级 15） | 30 / 480 分钟 |
| 建议 | —— | 30 / 720 分钟（升级 30） | 60 / 1440 分钟 |
| 商户纠纷 | 15 / 240 分钟（升级 15） | 20 / 480 分钟（升级 20） | 30 / 720 分钟 |
| 失物招领 | —— | —— | 60 / 1440 分钟 |

> 项目级示例：星港城购物中心「工程·报修·紧急」收紧到 5 / 90 分钟；云汇中心写字楼「工程·报修·高优」为 20 / 300 分钟；滨海广场商业街「秩序·投诉·高优」为 15 / 240 分钟。
> 表中未覆盖的组合会落到按优先级的兜底口径，`check.php` 的 C53b 会提醒补齐。
