hiwork-core 0.0.18 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/client.js +380 -2
- package/lib/identity.js +111 -0
- package/lib/index.js +1109 -13
- package/lib/memory-config.js +266 -0
- package/lib/pms-client.js +572 -0
- package/lib/pms-todo-bridge.js +640 -0
- package/lib/types/client/contracts.d.ts +22 -0
- package/lib/types/client/pms-todo.d.ts +357 -0
- package/lib/types/identity.d.ts +64 -0
- package/lib/types/memory-config.d.ts +113 -0
- package/lib/types/pms-client.d.ts +293 -0
- package/lib/types/pms-todo-bridge.d.ts +426 -0
- package/package.json +1 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PMS 服务基址 —— **全仓唯一一处**写死这个域名的地方。
|
|
3
|
+
*
|
|
4
|
+
* 为什么固定而不是配置项:PMS 是组织自建且**只有这一个生产部署**的内部系统(不像 ai-router 那样
|
|
5
|
+
* 需要自建/多环境),让每台终端在设置里填一个 URL 只会多一个能填错、又没人能集中改的地方;真要
|
|
6
|
+
* 换部署,改这一个常量、发一版即可。反过来说,**任何别处都不许再写这个域名**(包括文档示例之外
|
|
7
|
+
* 的代码、错误消息、日志),否则「唯一的基址」立刻变成两处能漂移的真相。
|
|
8
|
+
*/
|
|
9
|
+
export declare const PMS_BASE_URL = "https://pms.hivery.cn";
|
|
10
|
+
/**
|
|
11
|
+
* 单次请求超时:10 秒(spec §7 的冻结决策)。
|
|
12
|
+
* Host 是唯一会替界面等 PMS 的地方,超时不给足会让右栏面板一直转圈;给太多则用户以为卡死。
|
|
13
|
+
*/
|
|
14
|
+
export declare const PMS_REQUEST_TIMEOUT_MS = 10000;
|
|
15
|
+
/**
|
|
16
|
+
* access token 的**兜底**有效期:10 小时(= PMS `jwt.expiration=36000000`)。
|
|
17
|
+
* 只在响应缺 `expiresIn` 时用;正常路径一律按服务端给的 `expiresIn` 算过期时刻。
|
|
18
|
+
*/
|
|
19
|
+
export declare const PMS_ACCESS_TOKEN_TTL_MS: number;
|
|
20
|
+
/** 本客户端只有三个错误码:凭据失效 / 上游不可用 / 权限不足。其余(输入不合法等)由 Bridge 层判。 */
|
|
21
|
+
export type PmsErrorCode = 'UNAUTHORIZED' | 'UPSTREAM_UNAVAILABLE' | 'FORBIDDEN';
|
|
22
|
+
/** PMS 调用失败。`message` 一律是稳定文案,可直接展示给用户。 */
|
|
23
|
+
export declare class PmsError extends Error {
|
|
24
|
+
readonly code: PmsErrorCode;
|
|
25
|
+
readonly status?: number | undefined;
|
|
26
|
+
constructor(code: PmsErrorCode, message: string, status?: number | undefined);
|
|
27
|
+
}
|
|
28
|
+
/** 未登录 / 凭据失效的文案(与 Bridge 侧 `UNAUTHORIZED` 同口径:引导重新输入,不静默失败)。 */
|
|
29
|
+
export declare const PMS_UNAUTHORIZED_MESSAGE = "\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165 PMS \u8D26\u53F7\u5BC6\u7801";
|
|
30
|
+
/** 通用失败文案:不暴露任何 PMS 实现细节(baseURL / 路径 / 状态码 / 响应体)。 */
|
|
31
|
+
export declare const PMS_GENERIC_FAILURE_MESSAGE = "PMS \u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
32
|
+
/**
|
|
33
|
+
* 权限不足的**固定**文案(spec §3 D7 逐字要求):UI 必须明说「在 HiWork 里没有完成任务的权限」,
|
|
34
|
+
* 而不是静默失败或回滚得莫名其妙——用户该做的是去 PMS 页面操作。
|
|
35
|
+
*
|
|
36
|
+
* 与 `UNAUTHORIZED` 分开的理由:重新登录**不会**解决它(权限是角色给的),把两者混成一个码,
|
|
37
|
+
* 界面就只能给出「请重新输入密码」这种无效引导。
|
|
38
|
+
*/
|
|
39
|
+
export declare const PMS_FORBIDDEN_MESSAGE = "\u5728 HiWork \u91CC\u6CA1\u6709\u5B8C\u6210\u4EFB\u52A1\u7684\u6743\u9650\uFF0C\u8BF7\u5230 PMS \u64CD\u4F5C";
|
|
40
|
+
/**
|
|
41
|
+
* 完成任务时用的状态值(PMS `TaskVO.status`:0-待办 / 1-进行中 / 2-已完成 / 3-逾期)。
|
|
42
|
+
* 完成与激活走同一个 `PUT /{id}/status`,差别只有这个 query 值。
|
|
43
|
+
*/
|
|
44
|
+
export declare const PMS_TASK_STATUS_COMPLETED = 2;
|
|
45
|
+
/**
|
|
46
|
+
* 「我的任务」只取**未完成**的三种状态(spec §3 D7 的 `status=0&status=1&status=3`)。
|
|
47
|
+
* 2(已完成)不取:面板是待办视图,把做完的任务一起拉回来只会让列表变长。
|
|
48
|
+
* 顺序即 query 里的出现顺序(由测试钉死)。
|
|
49
|
+
*/
|
|
50
|
+
export declare const PMS_TASK_OPEN_STATUSES: readonly [0, 1, 3];
|
|
51
|
+
/** 「我的任务」的默认页大小(spec §3 D7)。50 条足够覆盖一个人的未完成任务,且一次请求就够。 */
|
|
52
|
+
export declare const PMS_TASK_PAGE_SIZE = 50;
|
|
53
|
+
/**
|
|
54
|
+
* 一次成功登录 / 刷新换来的会话。
|
|
55
|
+
*
|
|
56
|
+
* `accessToken` **绝不出 Host**;`expiresAt` 是绝对时刻(epoch ms)而不是剩余秒数——剩余秒数
|
|
57
|
+
* 一旦跨过任何一个 await 就开始漂,而「过期了没有」是授权路径上唯一的判据。
|
|
58
|
+
*/
|
|
59
|
+
export interface PmsTokenSet {
|
|
60
|
+
accessToken: string;
|
|
61
|
+
/** 7 天有效;PMS 没回时缺省(此时 access token 过期就只能重新输入账号密码)。 */
|
|
62
|
+
refreshToken?: string;
|
|
63
|
+
expiresAt: number;
|
|
64
|
+
/**
|
|
65
|
+
* PMS 的**用户 id**(登录响应 `data.userId`,spec §3 D7 要求登录时捕获)。
|
|
66
|
+
*
|
|
67
|
+
* 它**不是**凭据(是个人数字 id,PMS 侧内部的 `user_id`),但它是「我的任务」唯一的查询键
|
|
68
|
+
* (`assigneeId`),所以必须跟着会话一起存下来。`refresh` 的响应里也可能有,但**不以它为准**
|
|
69
|
+
* ——见 `src/pms-todo-bridge.ts` 的 `authorize`(刷新不改变 userId)。
|
|
70
|
+
*/
|
|
71
|
+
userId?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 一条待办(**冻结契约**,spec §4.2 的 `TodoItem`,字段名与 PMS 的 `TodoTaskVO` 一一对应)。
|
|
75
|
+
*
|
|
76
|
+
* 演进纪律(§4.2):**未知字段忽略、未知枚举原样透传**。`status` / `priority` 刻意不写成联合
|
|
77
|
+
* 类型——PMS 将来加一个 `URGENT` 优先级不该让老客户端崩,也不该让 Host 把它当成脏数据丢掉。
|
|
78
|
+
* 可选字段**按服务端给的有没有来**(没给就不写这个键),不由本层补 `undefined`。
|
|
79
|
+
*/
|
|
80
|
+
export interface PmsTodoItem {
|
|
81
|
+
id: number;
|
|
82
|
+
title: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
/** `TODO` | `TEMP`(原样透传)。 */
|
|
85
|
+
category?: string;
|
|
86
|
+
/** `HIGH` | `MEDIUM` | `LOW` | 服务端将来新增的值,一律原样透传。 */
|
|
87
|
+
priority?: string;
|
|
88
|
+
/** `PENDING` | `COMPLETED` | 服务端将来新增的值,一律原样透传。 */
|
|
89
|
+
status: string;
|
|
90
|
+
/** `yyyy-MM-dd`。 */
|
|
91
|
+
startDate?: string;
|
|
92
|
+
dueDate?: string;
|
|
93
|
+
completedTime?: string;
|
|
94
|
+
/** 服务端判定(`status='PENDING' AND due_date < CURDATE()`),**客户端不重算**。 */
|
|
95
|
+
isOverdue?: boolean;
|
|
96
|
+
/** 服务端判定(今日视图里 = 今天到期或无截止日期),**客户端不重算**。 */
|
|
97
|
+
isDueSoon?: boolean;
|
|
98
|
+
}
|
|
99
|
+
/** 勾选用的两个字面量(PMS 的 `updateTaskStatus` 只认这两个值)。 */
|
|
100
|
+
export type PmsTodoStatusValue = 'PENDING' | 'COMPLETED';
|
|
101
|
+
/**
|
|
102
|
+
* 一条**项目任务**(spec §3 D7;服务端 `pms-task` 的 `TaskVO` 投影)。字段类型按 PMS 源码定:
|
|
103
|
+
* `id` / `projectId` / `assigneeId` / `status` / `priority` / `auditStatus` 是数字,
|
|
104
|
+
* `deadline` 是 `yyyy-MM-dd HH:mm:ss`(PMS 的 `@JsonFormat`),其余是字符串,
|
|
105
|
+
* `estimatedHours` / `actualHours` 是 `BigDecimal`(Jackson 序列化成数字)。
|
|
106
|
+
*
|
|
107
|
+
* 演进纪律与 {@link PmsTodoItem} 同款,但**多一条**:任务的状态与优先级在 PMS 侧是**数字**
|
|
108
|
+
* (0/1/2/3 与 1/2/3),所以这里没有「未知枚举」可言——数字照发,`statusName` / `priorityName`
|
|
109
|
+
* 这两个给人看的名字原样透传(将来 PMS 加档位不该让老客户端崩)。未知**字段**忽略。
|
|
110
|
+
*
|
|
111
|
+
* 必填项(设计里没标 `?` 的那些)**一定会出现**,服务端没给时按类型兜底(`''` / `0`):
|
|
112
|
+
* 界面按 `status === 3` 判逾期、按 `projectName` 渲染徽标,少一个键就得让每个消费方各写一遍兜底。
|
|
113
|
+
* 设计里明确标可选的那五个(阶段名 / 需求名 / 模块名 / 两个工时)**只按服务端给没给来**。
|
|
114
|
+
*/
|
|
115
|
+
export interface PmsTaskItem {
|
|
116
|
+
id: number;
|
|
117
|
+
taskName: string;
|
|
118
|
+
description: string;
|
|
119
|
+
projectId: number;
|
|
120
|
+
projectName: string;
|
|
121
|
+
/** 所属项目阶段名(服务端没给就没有这个键)。 */
|
|
122
|
+
projectStageName?: string;
|
|
123
|
+
assigneeId: number;
|
|
124
|
+
assigneeName: string;
|
|
125
|
+
/** `yyyy-MM-dd HH:mm:ss`;没有截止时间时是空串。 */
|
|
126
|
+
deadline: string;
|
|
127
|
+
/** 0-待办 / 1-进行中 / 2-已完成 / 3-逾期。 */
|
|
128
|
+
status: number;
|
|
129
|
+
statusName: string;
|
|
130
|
+
/** 1-低 / 2-中 / 3-高。 */
|
|
131
|
+
priority: number;
|
|
132
|
+
priorityName: string;
|
|
133
|
+
taskType: string;
|
|
134
|
+
taskTypeName: string;
|
|
135
|
+
/** 0-待审核 / 1-已通过 / 2-已驳回。 */
|
|
136
|
+
auditStatus: number;
|
|
137
|
+
auditStatusName: string;
|
|
138
|
+
/** 关联需求名(服务端没给就没有这个键)。 */
|
|
139
|
+
requirementName?: string;
|
|
140
|
+
/** 关联功能模块名(服务端没给就没有这个键)。 */
|
|
141
|
+
moduleName?: string;
|
|
142
|
+
/** 预估工时(小时)。 */
|
|
143
|
+
estimatedHours?: number;
|
|
144
|
+
/** 实际工时(小时,PMS 从 `work_hour` 汇总)。 */
|
|
145
|
+
actualHours?: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 「我的任务」的返回(spec §3 D7 的**分页对象**投影,服务端是 `PageResult<TaskVO>`)。
|
|
149
|
+
*
|
|
150
|
+
* ⚠️ 与 `today` 的区别就在这里:待办的 `data` 是**数组**,任务的 `data` 是**分页对象**
|
|
151
|
+
* (`{records, total, current, size}`)——把两者当成同一个形状读,轻则列表永远空,重则把
|
|
152
|
+
* `records` 当 `undefined` 抛错。
|
|
153
|
+
*/
|
|
154
|
+
export interface PmsTaskList {
|
|
155
|
+
items: PmsTaskItem[];
|
|
156
|
+
/** 总条数(服务端缺省时回退成 `items.length`,免得界面显示「共 0 条」却列着内容)。 */
|
|
157
|
+
total: number;
|
|
158
|
+
current: number;
|
|
159
|
+
size: number;
|
|
160
|
+
}
|
|
161
|
+
/** 「我的任务」的可选项(`size` 默认 {@link PMS_TASK_PAGE_SIZE},非正整数一律回退缺省值)。 */
|
|
162
|
+
export interface PmsMyTasksOptions {
|
|
163
|
+
size?: number;
|
|
164
|
+
}
|
|
165
|
+
/** `PUT /api/task/tasks/{id}/status` 的结果(PMS 这个接口回 void,所以按请求值回退)。 */
|
|
166
|
+
export interface PmsTaskStatusResult {
|
|
167
|
+
id: number;
|
|
168
|
+
/** 任务状态数字(完成是 {@link PMS_TASK_STATUS_COMPLETED})。 */
|
|
169
|
+
status: number;
|
|
170
|
+
}
|
|
171
|
+
/** `GET /api/todo/statistics` 的投影(spec §D4)。 */
|
|
172
|
+
export interface PmsTodoStatistics {
|
|
173
|
+
total: number;
|
|
174
|
+
pending: number;
|
|
175
|
+
completed: number;
|
|
176
|
+
overdue: number;
|
|
177
|
+
}
|
|
178
|
+
/** `PUT /api/todo/{id}/status` 的结果(服务端缺 `completedTime` 时缺省)。 */
|
|
179
|
+
export interface PmsTodoStatusResult {
|
|
180
|
+
id: number;
|
|
181
|
+
status: PmsTodoStatusValue;
|
|
182
|
+
completedTime?: string;
|
|
183
|
+
}
|
|
184
|
+
/** 构造选项(全部可选;生产只传 `log`)。 */
|
|
185
|
+
export interface PmsClientOptions {
|
|
186
|
+
/** 基址覆盖(**测试用**);缺省 {@link PMS_BASE_URL}。生产不许传。 */
|
|
187
|
+
baseURL?: string;
|
|
188
|
+
/** 超时覆盖(测试用);缺省 {@link PMS_REQUEST_TIMEOUT_MS}。 */
|
|
189
|
+
timeoutMs?: number;
|
|
190
|
+
/** 时钟注入(测试用);缺省 `Date.now`。 */
|
|
191
|
+
now?: () => number;
|
|
192
|
+
/**
|
|
193
|
+
* Host 日志出口。**只记事件与业务 code**——用户可见的 message 与响应体都不许进来
|
|
194
|
+
* (§7:账号密码与 JWT 不进日志;业务 message 可能带用户数据)。
|
|
195
|
+
*/
|
|
196
|
+
log?: ((message: string) => void) | undefined;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* PMS HTTP 客户端。零 DSH 依赖、零凭据——它只负责「把一次调用发出去、把结果或稳定错误返回」。
|
|
200
|
+
*
|
|
201
|
+
* @param options - 见 {@link PmsClientOptions}。
|
|
202
|
+
*/
|
|
203
|
+
export declare class PmsClient {
|
|
204
|
+
private readonly baseURL;
|
|
205
|
+
private readonly timeoutMs;
|
|
206
|
+
private readonly now;
|
|
207
|
+
private readonly log;
|
|
208
|
+
constructor(options?: PmsClientOptions);
|
|
209
|
+
/**
|
|
210
|
+
* `POST /api/auth/login`:用账号密码换 JWT(access + refresh)。
|
|
211
|
+
*
|
|
212
|
+
* 失败语义(由 {@link PmsClient.call} 统一给出):
|
|
213
|
+
* - 账号密码不对 → `UNAUTHORIZED`(HTTP 401 **或** 信封 `code:401`,两者同义);
|
|
214
|
+
* - 其余(网络 / 超时 / 5xx / 非业务信封)→ `UPSTREAM_UNAVAILABLE`。
|
|
215
|
+
* **本方法不重试**,调用方也不许替它重试(§7:避免用错密码连续触发 PMS 的失败锁定)。
|
|
216
|
+
*/
|
|
217
|
+
login(username: string, password: string): Promise<PmsTokenSet>;
|
|
218
|
+
/**
|
|
219
|
+
* `POST /api/auth/refresh`:用 refresh token 续一次 access token。
|
|
220
|
+
*
|
|
221
|
+
* refresh token 失效(7 天到期 / 已被 PMS 撤销)→ `UNAUTHORIZED`,此时**只能**重新输入账号密码;
|
|
222
|
+
* 网络问题 → `UPSTREAM_UNAVAILABLE`(那是「稍后重试」,不是「要重新登录」——两者对用户是两件事)。
|
|
223
|
+
*/
|
|
224
|
+
refresh(refreshToken: string): Promise<PmsTokenSet>;
|
|
225
|
+
/**
|
|
226
|
+
* `GET /api/todo/today`:今日待办(⚠️ 口径比名字宽:**今天 + 明天 + 无截止日期**都算,spec §D4)。
|
|
227
|
+
*
|
|
228
|
+
* `data` 必须是数组;不是数组时按**上游不可用**处理(绝不读成「今天没有待办」——那是把中间层的
|
|
229
|
+
* 维护页当成空清单,用户会以为自己清空了所有待办)。缺 `id` 的条目跳过:没有 id 就没法勾选,
|
|
230
|
+
* 渲染出来只会是一个点了没反应的复选框。
|
|
231
|
+
*/
|
|
232
|
+
today(accessToken: string): Promise<PmsTodoItem[]>;
|
|
233
|
+
/** `GET /api/todo/statistics`:total / pending / completed / overdue 四个计数。 */
|
|
234
|
+
statistics(accessToken: string): Promise<PmsTodoStatistics>;
|
|
235
|
+
/**
|
|
236
|
+
* `PUT /api/todo/{id}/status?status=PENDING|COMPLETED`:勾完成 / 取消完成。
|
|
237
|
+
*
|
|
238
|
+
* `status` 走 **query**(PMS 侧就是这么定的),所以必须 `encodeURIComponent`——虽然本层只放行
|
|
239
|
+
* 两个字面量、编码后与原值相同,但「拼进 URL 的值一律编码」是纪律:少一次编码在参数集合变化时
|
|
240
|
+
* 就是一个注入面。
|
|
241
|
+
*
|
|
242
|
+
* ⚠️ 副作用(spec §D5):勾完成时若来源是项目任务且该任务下再无 PENDING 待办,**PMS 会把项目
|
|
243
|
+
* 任务一并标记完成**;反向不成立。本层只负责发请求,提示文案属 UI 的职责。
|
|
244
|
+
*
|
|
245
|
+
* 返回值以服务端回声为准(`data.id` / `data.status`),`data` 为空(PMS 这个接口可能回 void)
|
|
246
|
+
* 时回退成请求里的 id / status——调用方要的是「我勾的是哪条、现在是什么状态」,两者都知道。
|
|
247
|
+
*/
|
|
248
|
+
setStatus(accessToken: string, id: number, status: PmsTodoStatusValue): Promise<PmsTodoStatusResult>;
|
|
249
|
+
/**
|
|
250
|
+
* `GET /api/task/tasks?assigneeId={userId}&status=0&status=1&status=3¤t=1&size=50`
|
|
251
|
+
* (spec §3 D7):**我的未完成项目任务**。
|
|
252
|
+
*
|
|
253
|
+
* 为什么必须直接查任务而不是只读待办:PMS 只在**任务创建那一刻**生成待办
|
|
254
|
+
* (`TaskService.createFromTask`),转派、走 SQL 批量建的历史任务、以及机制上线前的任务都
|
|
255
|
+
* 没有待办——只读 `todo_task` 会让这些任务在面板上凭空消失(§3 D7 的缺口表)。
|
|
256
|
+
*
|
|
257
|
+
* `status` 在 PMS 侧是 `List<Integer>`,所以是**多个同名参数**而不是逗号分隔(`status=0&status=1&status=3`);
|
|
258
|
+
* 顺序按 {@link PMS_TASK_OPEN_STATUSES},`assigneeId` 是**当前登录人的 PMS userId**
|
|
259
|
+
* (由调用方从凭据记录里取,见 `src/pms-todo-bridge.ts`)。
|
|
260
|
+
*
|
|
261
|
+
* ⚠️ 响应的 `data` 是**分页对象**(`{records, total, current, size}`)**不是数组**:
|
|
262
|
+
* 不是对象或 `records` 不是数组时按**上游不可用**处理(绝不读成「我没有任务」——那会让用户
|
|
263
|
+
* 以为活都干完了,而这正是本次最要避免的误读)。
|
|
264
|
+
*
|
|
265
|
+
* @param accessToken - Host 内存里的 PMS JWT。
|
|
266
|
+
* @param userId - 我的 PMS userId(凭据记录里的 `userId`)。
|
|
267
|
+
* @param opts - 见 {@link PmsMyTasksOptions}。
|
|
268
|
+
*/
|
|
269
|
+
myTasks(accessToken: string, userId: number, opts?: PmsMyTasksOptions): Promise<PmsTaskList>;
|
|
270
|
+
/**
|
|
271
|
+
* `PUT /api/task/tasks/{taskId}/status?status=2`:**完成一条项目任务**(spec §3 D7)。
|
|
272
|
+
*
|
|
273
|
+
* 与 {@link PmsClient.setStatus} 同款:`status` 走 **query** 且被编码;要求业务 `code === 200`,
|
|
274
|
+
* `data` 容许为 `null`(PMS 这个接口的返回类型是 `Result<Void>`,实测就是 null),此时回退成
|
|
275
|
+
* 请求里的 id / 状态数字——调用方要的是「我完成的是哪条、现在是什么状态」,两者都知道。
|
|
276
|
+
*
|
|
277
|
+
* ⚠️ 权限:这个端点带 `@PreAuthorize("hasAuthority('task:list:edit')")`,没有该权限的角色会失败
|
|
278
|
+
* (`FORBIDDEN`,见 {@link PMS_FORBIDDEN_MESSAGE})。这与待办那条不同——**不要**把它当成「凭据
|
|
279
|
+
* 过期」让用户重新登录。
|
|
280
|
+
*/
|
|
281
|
+
completeTask(accessToken: string, taskId: number): Promise<PmsTaskStatusResult>;
|
|
282
|
+
/**
|
|
283
|
+
* 发一次调用并解开 `Result` 信封。**所有错误都在这里收敛**,返回值只有 `data`。
|
|
284
|
+
*
|
|
285
|
+
* 顺序是刻意的:**先看 HTTP 401,再解信封**——PMS 未登录时回的是 Spring Security 形状
|
|
286
|
+
* (`{path, code:"AUTH_001", message}`),它的 `message` 是给浏览器看跳登录页的,既不是业务文案
|
|
287
|
+
* 也可能带 `path` 等实现细节。把它当业务信封解析,轻则文案变成一句英文,重则把「凭据失效」
|
|
288
|
+
* 误判成「上游故障」而让用户不知道要重新输入密码。
|
|
289
|
+
*/
|
|
290
|
+
private call;
|
|
291
|
+
/** 登录 / 刷新的 `data` → {@link PmsTokenSet}。200 却不给 token 视为契约破坏(上游不可用)。 */
|
|
292
|
+
private tokenSet;
|
|
293
|
+
}
|