# 员工处罚待确认弹窗

> pending-popup 接口：每日 14:00 批次、未登录则登录即弹、多端只弹一次均由后端判定；含调用时机、14 点定时参考实现与弹窗交互说明

- 类型：前端对接
- 分类：仓库配货 / 打包
- 端：仓管小程序
- 后端服务：supplier
- 状态：已上测
- 更新：2026-10-09
- 网页版：https://doc.xfun.fun/wms-punish-popup-api

## 总览

- 新增 **1 个接口** `POST /wms-punish-appeal/pending-popup`，无请求体，返回「是否弹窗 + 待处理笔数 + 待确认金额」。
- **「该不该弹」全部由后端判断**（14:00 批次、每批次只弹一次、多端去重）。前端只负责：在约定时机调接口，`showFlag=1` 就弹，否则什么都不做。
- 前端 **不需要**本地记录「今天弹过没有」，也不需要自己判断时间是否过了 14:00，重复调用是安全的。
- 处罚数据口径：当前登录人作为责任人、**待确认**、**不在申诉中**，且 **创建不满 48 小时**（仍在申诉期内）的处罚。`10-12 调整` 超过 48 小时已不能申诉，也会被系统自动确认，因此不再提醒。

## 接口定义

### POST /wms-punish-appeal/pending-popup

**新增**

查询处罚待确认弹窗。鉴权方式与「我的处罚列表」 `/wms-punish-appeal/list` 相同（Header 带 `Authorization`）。

#### 请求参数

无。Body 可传空对象 `{}` 或不传。

#### 响应 data 字段

| 字段 | 类型 | 说明 |
|----|----|----|
| `showFlag` | Integer | 是否弹窗：**1 = 弹**，0 = 不弹 |
| `pendingCount` | Long | 待处理处罚笔数（showFlag=0 时为 0） |
| `pendingAmount` | BigDecimal | 待确认金额，单位元，已保留 2 位小数（showFlag=0 时为 0） |

#### 响应示例：需要弹窗

```json
{
  "code": 0,
  "msg": "success",
  "data": { "showFlag": 1, "pendingCount": 3, "pendingAmount": 150.00 }
}
```

#### 响应示例：不弹窗

```json
{
  "code": 0,
  "msg": "success",
  "data": { "showFlag": 0, "pendingCount": 0, "pendingAmount": 0 }
}
```

> [!NOTE]
> 判断是否弹窗 **只看 `showFlag`**，不要用 `pendingCount > 0` 判断：已经弹过的批次后端会返回 showFlag=0、pendingCount=0，即使用户仍有待处理处罚。

> [!WARNING]
> 金额 JSON 中是数字，前端展示时请统一格式化为两位小数（`Number(v).toFixed(2)`），例如 `150` 显示为 `¥150.00`。

## 调用时机（前端要做的）

| 时机 | 说明 |
|----|----|
| ① 登录成功后 | 对应需求「未登录的用户，登录时立即弹」 |
| ② 小程序 `onShow`（App 级或首页） | 带着本地 token 冷启动、从后台切回前台都算「进入系统」；也用于补偿 14:00 时小程序在后台、定时器没跑的情况 |
| ③ 在线定时：当天 14:00 + 随机 0~60 秒 | 对应需求「已登录的用户 14:00 弹一次」。加随机延迟是为了避免所有在线用户同一秒请求 |

#### 参考实现（伪代码，按项目框架调整）

```js
// 统一入口：任何时机都只调这个函数，后端负责去重
async function checkPunishPopup() {
  if (!getToken()) return;
  const res = await request.post('/wms-punish-appeal/pending-popup', {});
  const d = res && res.data;
  if (d && d.showFlag === 1) {
    showPunishPopup(d.pendingCount, Number(d.pendingAmount).toFixed(2));
  }
}

// 时机③：在线时到 14:00 触发一次
let popupTimer = null;
function scheduleTwoPm() {
  clearTimeout(popupTimer);
  const now = new Date();
  const target = new Date(now);
  target.setHours(14, 0, 0, 0);
  if (now >= target) target.setDate(target.getDate() + 1);   // 今天已过 14 点则排到明天
  const jitter = Math.floor(Math.random() * 60 * 1000);       // 0~60 秒随机打散
  popupTimer = setTimeout(() => { checkPunishPopup(); scheduleTwoPm(); },
                          target - now + jitter);
}

// 登录成功回调里：checkPunishPopup(); scheduleTwoPm();
// onShow 里：      checkPunishPopup(); scheduleTwoPm();   // 切回前台时重排定时器
// 退出登录 / onHide：clearTimeout(popupTimer);
```

> [!NOTE]
> 设备本地时间不准没关系：定时器只决定「什么时候问一次」，弹不弹以后端（北京时间）为准。最坏情况是晚一点在下次 onShow 时弹出。

## 后端判定规则（了解即可）

后端以 **「最近一个已经过去的 14:00」**作为提醒批次，每个用户每个批次最多弹一次：当前 ≥ 今天 14:00 → 批次是今天；当前 \< 今天 14:00 → 批次是昨天。

#### 一个用户的一天（假设一直有 2 笔待确认处罚）

| 时间 | 发生了什么 |
|----|----|
| 09:30 | 登录 → 昨天的批次已经弹过 → 不弹 |
| 14:00:23 | 在线定时器触发 → 今天批次未弹 → 弹（显示 2 笔） |
| 15:10 | 切回前台 onShow → 今天批次已弹 → 不弹 |
| 16:00 | 另一台手机登录同一账号 → 今天批次已弹 → 不弹（多端只弹一次） |
| 次日 08:50 | 登录 → 批次仍是「昨天」，已弹过 → 不弹；等到当天 14:00 再弹 |

#### 其他情况

- 昨天 14:00 后一直没上线、今天上午才登录 → 昨天批次没弹过 → 登录即弹。
- 14:00 时没有待处理处罚 → 不弹，且 **不占用**本批次；15:00 新来了一笔处罚，下次 onShow 时 → 弹。
- 只有「申诉中」的处罚 → 不弹（员工此时无需操作）。

## 弹窗交互

| 元素 | 内容 / 行为 |
|----|----|
| 标题 | 您有处罚待确认 |
| 说明 | 您当前有处罚订单待确认，请及时前往处理。逾期未确认，系统将按规则自动确认。 |
| 待处理订单 | `{pendingCount}`笔 |
| 待确认金额 | ¥ `{pendingAmount}`（两位小数） |
| 提示条 | 请尽快确认处罚信息，如有异议可发起申诉 |
| 【前往处理】 | 关闭弹窗 → 跳转「我的处罚」列表，**默认筛选待确认**（列表接口 `/wms-punish-appeal/list` 传 `status=0`） |
| 【稍后处理】 | 直接关闭，不调接口 |
| 底部小字 | 本弹窗仅作消息提醒，具体处罚信息以处罚详情为准 |
| 自动关闭 | 弹出后 **3 秒无任何操作**自动关闭；用户在 3 秒内 **触碰弹窗任意位置**即取消倒计时，之后只能手动关闭 |

> [!NOTE]
> 文案全部前端写死，接口只返回两个数字。弹窗关闭（无论哪种方式）都 **不需要**回调后端：后端在返回 showFlag=1 的同时就已记为「已弹」。

## 联调注意

- 测试环境已部署，用员工账号（有待确认处罚的责任人）登录即可联调。
- 同一账号同一批次只会弹一次。**想重复测试，调用下面的重置接口即可**，任何时间点重置后都能立即再弹。

#### 测试环境专用：重置弹窗标记 `10-12 新增`

### POST /test/wms-punish-popup/reset

- 无请求体，Header 带当前员工的 `Authorization`（与弹窗接口同一个 token），只清 **当前登录账号**的标记。
- 成功返回 `{"code":0,"msg":"success"}`；生产环境调用会报「不允许操作！」，**正式代码里不要调用**。

#### 调试循环

```text
POST /test/wms-punish-popup/reset     // 清标记
POST /wms-punish-appeal/pending-popup // → showFlag=1，弹窗
POST /wms-punish-appeal/pending-popup // → showFlag=0（已弹过）
POST /test/wms-punish-popup/reset     // 再清，下一次又会弹 …
```

调试期间可以在开发版里临时加一个「重置弹窗」按钮调用它；也可以用 curl：

```bash
curl -X POST '<测试环境域名>/test/wms-punish-popup/reset' -H 'Authorization: <员工token>'
```

- 没有 **48 小时内**待确认处罚的账号永远返回 showFlag=0。测试库老数据大多已超过 48 小时，**联调前请先造新数据**：在后台「巡线处罚」给该员工创建一笔处罚（新建默认待确认），48 小时内可反复配合重置接口测试。
- 弹窗只统计 48 小时内的处罚，「我的处罚」列表的待确认筛选会多出已过申诉期、尚未被自动确认的处罚（线上最多多停留约一天；测试环境不跑自动确认，会多出很多）。所以 **列表条数 ≥ 弹窗笔数是正常的**。
