@steedos-labs/content-compliance 0.8.26 → 0.8.27

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.
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var tslib_1 = require("tslib");
4
+ var fs = require("fs");
5
+ var path = require("path");
6
+ var yaml = require("js-yaml");
7
+ var lodash = require("lodash");
8
+ var triggerPath = path.resolve(__dirname, '../../..', 'main/default/triggers/pepsico_material_approval_change.trigger.yml');
9
+ var AsyncFunction = Object.getPrototypeOf(function () {
10
+ return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) {
11
+ return [2];
12
+ }); });
13
+ }).constructor;
14
+ var loadHandler = function () {
15
+ var meta = yaml.safeLoad(fs.readFileSync(triggerPath, 'utf8'));
16
+ return new AsyncFunction('ctx', 'global', 'process', 'console', meta.handler);
17
+ };
18
+ var MATRIX_ROW = {
19
+ _id: 'matrix-1',
20
+ ns_reviewer: ['ns-u'],
21
+ ns_assistance: ['ns-a'],
22
+ legal_reviewer: ['legal-u'],
23
+ sra_reviewer: ['sra-u'],
24
+ ca_reviewer: ['ca-u'],
25
+ qc_reviewer: ['qc-u'],
26
+ can_pass_users: ['skip-u'],
27
+ pre_ns_reviewer: 'pre-ns-u',
28
+ reviewer: ['fixed-u']
29
+ };
30
+ var buildCtx = function (_a) {
31
+ var doc = _a.doc, _b = _a.previousDoc, previousDoc = _b === void 0 ? null : _b, _c = _a.isInsert, isInsert = _c === void 0 ? true : _c, _d = _a.matrixRows, matrixRows = _d === void 0 ? [] : _d;
32
+ var logsStub = { directInsert: jest.fn().mockResolvedValue({}) };
33
+ var reviewerStub = { directFind: jest.fn().mockResolvedValue(matrixRows) };
34
+ var fallback = { directFind: jest.fn().mockResolvedValue([]), directInsert: jest.fn().mockResolvedValue({}) };
35
+ var ctx = {
36
+ params: { doc: doc, previousDoc: previousDoc, isInsert: isInsert, userId: 'submitter-u', spaceId: 'space-1', id: doc._id },
37
+ getObject: jest.fn(function (name) {
38
+ if (name === 'pepsico_reviewer')
39
+ return reviewerStub;
40
+ if (name === 'logs')
41
+ return logsStub;
42
+ return fallback;
43
+ })
44
+ };
45
+ return { ctx: ctx, logsStub: logsStub, reviewerStub: reviewerStub };
46
+ };
47
+ var run = function (ctx, env) {
48
+ if (env === void 0) { env = {}; }
49
+ return loadHandler()(ctx, { _: lodash }, { env: env }, { error: jest.fn(), log: jest.fn() });
50
+ };
51
+ var baseDoc = function (overrides) {
52
+ if (overrides === void 0) { overrides = {}; }
53
+ return (tslib_1.__assign({ _id: 'approval-1', name: '2609095607', space: 'space-1', classification: 'MD-SC1-SC16', product: '16541517620103133', owner_department: 'org-1' }, overrides));
54
+ };
55
+ describe('pepsico_material_approval_change 保存时校验审核人 (#450)', function () {
56
+ it('新建时审核人全空 -> 按角色矩阵补齐, 放行', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
57
+ var doc, _a, ctx, reviewerStub, filters;
58
+ return tslib_1.__generator(this, function (_b) {
59
+ switch (_b.label) {
60
+ case 0:
61
+ doc = baseDoc({ approval_type: 'departmental_review' });
62
+ _a = buildCtx({ doc: doc, matrixRows: [MATRIX_ROW] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
63
+ return [4, run(ctx)];
64
+ case 1:
65
+ _b.sent();
66
+ expect(doc).toMatchObject({
67
+ ns_reviewer: ['ns-u'],
68
+ legal_reviewer: ['legal-u'],
69
+ sra_reviewer: ['sra-u'],
70
+ ca_reviewer: ['ca-u'],
71
+ ns_assistance: ['ns-a'],
72
+ pre_ns_reviewer: 'pre-ns-u',
73
+ can_pass_users: ['skip-u']
74
+ });
75
+ filters = reviewerStub.directFind.mock.calls[0][0].filters;
76
+ expect(filters).toEqual(expect.arrayContaining([
77
+ ['classification', '=', 'MD-SC1-SC16'],
78
+ ['product', '=', '16541517620103133'],
79
+ ['org', '=', 'org-1']
80
+ ]));
81
+ return [2];
82
+ }
83
+ });
84
+ }); });
85
+ it('approval_type 为空也要校验 —— 旧逻辑正是在这里整体跳过的', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
86
+ var doc, ctx;
87
+ return tslib_1.__generator(this, function (_a) {
88
+ switch (_a.label) {
89
+ case 0:
90
+ doc = baseDoc();
91
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
92
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少NS、Legal、SRA、CA审核人/)];
93
+ case 1:
94
+ _a.sent();
95
+ return [2];
96
+ }
97
+ });
98
+ }); });
99
+ it('approval_type 为空但四部门齐全 -> 放行', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
100
+ var doc, ctx;
101
+ return tslib_1.__generator(this, function (_a) {
102
+ switch (_a.label) {
103
+ case 0:
104
+ doc = baseDoc({
105
+ ns_reviewer: ['a'], legal_reviewer: ['b'], sra_reviewer: ['c'], ca_reviewer: ['d']
106
+ });
107
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
108
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
109
+ case 1:
110
+ _a.sent();
111
+ return [2];
112
+ }
113
+ });
114
+ }); });
115
+ it('approval_type 为空但已有固定审核人 -> 放行(走固定人员分支)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
116
+ var doc, ctx;
117
+ return tslib_1.__generator(this, function (_a) {
118
+ switch (_a.label) {
119
+ case 0:
120
+ doc = baseDoc({ reviewer: ['fixed-u'] });
121
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
122
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
123
+ case 1:
124
+ _a.sent();
125
+ return [2];
126
+ }
127
+ });
128
+ }); });
129
+ it('草稿更新时只校验合并后的记录, 不碰审核人的编辑不会被误拦', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
130
+ var doc, previousDoc, _a, ctx, reviewerStub;
131
+ return tslib_1.__generator(this, function (_b) {
132
+ switch (_b.label) {
133
+ case 0:
134
+ doc = { remark: '改个备注' };
135
+ previousDoc = baseDoc({
136
+ current_step: 'draft',
137
+ approval_type: 'departmental_review',
138
+ ns_reviewer: ['a'], legal_reviewer: ['b'], sra_reviewer: ['c'], ca_reviewer: ['d']
139
+ });
140
+ _a = buildCtx({ doc: doc, previousDoc: previousDoc, isInsert: false, matrixRows: [MATRIX_ROW] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
141
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
142
+ case 1:
143
+ _b.sent();
144
+ expect(doc.ns_reviewer).toBeUndefined();
145
+ expect(reviewerStub.directFind).toHaveBeenCalled();
146
+ return [2];
147
+ }
148
+ });
149
+ }); });
150
+ it('草稿更新时把审核人清空 -> 拦下(旧逻辑 update 完全不校验)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
151
+ var doc, previousDoc, ctx;
152
+ return tslib_1.__generator(this, function (_a) {
153
+ switch (_a.label) {
154
+ case 0:
155
+ doc = { ns_reviewer: [], legal_reviewer: [], sra_reviewer: [], ca_reviewer: [] };
156
+ previousDoc = baseDoc({
157
+ current_step: 'draft',
158
+ approval_type: 'departmental_review',
159
+ ns_reviewer: ['a'], legal_reviewer: ['b'], sra_reviewer: ['c'], ca_reviewer: ['d']
160
+ });
161
+ ctx = buildCtx({ doc: doc, previousDoc: previousDoc, isInsert: false, matrixRows: [] }).ctx;
162
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少NS、Legal、SRA、CA审核人/)];
163
+ case 1:
164
+ _a.sent();
165
+ return [2];
166
+ }
167
+ });
168
+ }); });
169
+ it('只补空, 不覆盖已有值', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
170
+ var doc, ctx;
171
+ return tslib_1.__generator(this, function (_a) {
172
+ switch (_a.label) {
173
+ case 0:
174
+ doc = baseDoc({ approval_type: 'departmental_review', ns_reviewer: ['manual-ns'] });
175
+ ctx = buildCtx({ doc: doc, matrixRows: [MATRIX_ROW] }).ctx;
176
+ return [4, run(ctx)];
177
+ case 1:
178
+ _a.sent();
179
+ expect(doc.ns_reviewer).toEqual(['manual-ns']);
180
+ expect(doc.ca_reviewer).toEqual(['ca-u']);
181
+ return [2];
182
+ }
183
+ });
184
+ }); });
185
+ it('矩阵命中但该格没配全 -> 指明缺哪几个部门', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
186
+ var doc, ctx;
187
+ return tslib_1.__generator(this, function (_a) {
188
+ switch (_a.label) {
189
+ case 0:
190
+ doc = baseDoc({ approval_type: 'departmental_review' });
191
+ ctx = buildCtx({ doc: doc, matrixRows: [{ _id: 'matrix-2', ns_reviewer: ['ns-u'] }] }).ctx;
192
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少Legal、SRA、CA审核人/)];
193
+ case 1:
194
+ _a.sent();
195
+ return [2];
196
+ }
197
+ });
198
+ }); });
199
+ it('ec_has_detail_page=yes 时 QC 也必填', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
200
+ var doc, ctx;
201
+ return tslib_1.__generator(this, function (_a) {
202
+ switch (_a.label) {
203
+ case 0:
204
+ doc = baseDoc({ approval_type: 'departmental_review', ec_has_detail_page: 'yes' });
205
+ ctx = buildCtx({ doc: doc, matrixRows: [tslib_1.__assign(tslib_1.__assign({}, MATRIX_ROW), { qc_reviewer: null })] }).ctx;
206
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少QC审核人/)];
207
+ case 1:
208
+ _a.sent();
209
+ return [2];
210
+ }
211
+ });
212
+ }); });
213
+ it('固定人员审核只要求 reviewer', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
214
+ var doc, ctx;
215
+ return tslib_1.__generator(this, function (_a) {
216
+ switch (_a.label) {
217
+ case 0:
218
+ doc = baseDoc({ approval_type: 'personnel_review' });
219
+ ctx = buildCtx({ doc: doc, matrixRows: [{ _id: 'matrix-3', reviewer: ['fixed-u'] }] }).ctx;
220
+ return [4, run(ctx)];
221
+ case 1:
222
+ _a.sent();
223
+ expect(doc.reviewer).toEqual(['fixed-u']);
224
+ return [2];
225
+ }
226
+ });
227
+ }); });
228
+ it('固定人员审核缺 reviewer -> 拦下', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
229
+ var doc, ctx;
230
+ return tslib_1.__generator(this, function (_a) {
231
+ switch (_a.label) {
232
+ case 0:
233
+ doc = baseDoc({ approval_type: 'personnel_review' });
234
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
235
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少固定审核人/)];
236
+ case 1:
237
+ _a.sent();
238
+ return [2];
239
+ }
240
+ });
241
+ }); });
242
+ it('DCO 分类跳过校验, 不查矩阵', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
243
+ var doc, _a, ctx, reviewerStub;
244
+ return tslib_1.__generator(this, function (_b) {
245
+ switch (_b.label) {
246
+ case 0:
247
+ doc = baseDoc({ classification: 'MD-PO1-PO18' });
248
+ _a = buildCtx({ doc: doc, matrixRows: [] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
249
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
250
+ case 1:
251
+ _b.sent();
252
+ expect(reviewerStub.directFind).not.toHaveBeenCalled();
253
+ return [2];
254
+ }
255
+ });
256
+ }); });
257
+ it('DCO 分类可由 CONTENT_DCO_TYPES 覆盖', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
258
+ var doc, _a, ctx, reviewerStub;
259
+ return tslib_1.__generator(this, function (_b) {
260
+ switch (_b.label) {
261
+ case 0:
262
+ doc = baseDoc({ classification: 'X-1' });
263
+ _a = buildCtx({ doc: doc, matrixRows: [] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
264
+ return [4, expect(run(ctx, { CONTENT_DCO_TYPES: 'X-1,Y-2' })).resolves.toBeUndefined()];
265
+ case 1:
266
+ _b.sent();
267
+ expect(reviewerStub.directFind).not.toHaveBeenCalled();
268
+ return [2];
269
+ }
270
+ });
271
+ }); });
272
+ it('只管草稿: 已进入流程的单保存时不校验', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
273
+ var inFlight, inFlight_1, inFlight_1_1, step, doc, previousDoc, _a, ctx, reviewerStub, e_1_1;
274
+ var e_1, _b;
275
+ return tslib_1.__generator(this, function (_c) {
276
+ switch (_c.label) {
277
+ case 0:
278
+ inFlight = ['ai_review', 'departmental_review', 'personnel_review', 'submitter_revisions', 'awaiting_deployment', 'conclusion'];
279
+ _c.label = 1;
280
+ case 1:
281
+ _c.trys.push([1, 6, 7, 8]);
282
+ inFlight_1 = tslib_1.__values(inFlight), inFlight_1_1 = inFlight_1.next();
283
+ _c.label = 2;
284
+ case 2:
285
+ if (!!inFlight_1_1.done) return [3, 5];
286
+ step = inFlight_1_1.value;
287
+ doc = { remark: '流程中改点东西' };
288
+ previousDoc = baseDoc({ current_step: step });
289
+ _a = buildCtx({ doc: doc, previousDoc: previousDoc, isInsert: false, matrixRows: [] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
290
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
291
+ case 3:
292
+ _c.sent();
293
+ expect(reviewerStub.directFind).not.toHaveBeenCalled();
294
+ _c.label = 4;
295
+ case 4:
296
+ inFlight_1_1 = inFlight_1.next();
297
+ return [3, 2];
298
+ case 5: return [3, 8];
299
+ case 6:
300
+ e_1_1 = _c.sent();
301
+ e_1 = { error: e_1_1 };
302
+ return [3, 8];
303
+ case 7:
304
+ try {
305
+ if (inFlight_1_1 && !inFlight_1_1.done && (_b = inFlight_1.return)) _b.call(inFlight_1);
306
+ }
307
+ finally { if (e_1) throw e_1.error; }
308
+ return [7];
309
+ case 8: return [2];
310
+ }
311
+ });
312
+ }); });
313
+ it('已中止的草稿不再校验, 避免挡住收尾操作', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
314
+ var doc, previousDoc, _a, ctx, reviewerStub;
315
+ return tslib_1.__generator(this, function (_b) {
316
+ switch (_b.label) {
317
+ case 0:
318
+ doc = { remark: 'x' };
319
+ previousDoc = baseDoc({ current_step: 'draft', review_status: 'terminated' });
320
+ _a = buildCtx({ doc: doc, previousDoc: previousDoc, isInsert: false, matrixRows: [] }), ctx = _a.ctx, reviewerStub = _a.reviewerStub;
321
+ return [4, expect(run(ctx)).resolves.toBeUndefined()];
322
+ case 1:
323
+ _b.sent();
324
+ expect(reviewerStub.directFind).not.toHaveBeenCalled();
325
+ return [2];
326
+ }
327
+ });
328
+ }); });
329
+ it('被拦下时把原始ID写进系统日志供管理员定位', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
330
+ var doc, _a, ctx, logsStub, logged;
331
+ return tslib_1.__generator(this, function (_b) {
332
+ switch (_b.label) {
333
+ case 0:
334
+ doc = baseDoc({ approval_type: 'departmental_review' });
335
+ _a = buildCtx({ doc: doc, matrixRows: [] }), ctx = _a.ctx, logsStub = _a.logsStub;
336
+ return [4, run(ctx).catch(function () { return null; })];
337
+ case 1:
338
+ _b.sent();
339
+ expect(logsStub.directInsert).toHaveBeenCalled();
340
+ logged = JSON.parse(logsStub.directInsert.mock.calls[0][0].data);
341
+ expect(logged.action).toBe('save-blocked-reviewer-missing');
342
+ expect(logged.approval_name).toBe('2609095607');
343
+ expect(logged.owner_department).toBe('org-1');
344
+ expect(logged.classification).toBe('MD-SC1-SC16');
345
+ expect(logged.missing).toEqual(['NS', 'Legal', 'SRA', 'CA']);
346
+ return [2];
347
+ }
348
+ });
349
+ }); });
350
+ it('写日志失败不能把校验本身搞崩, 仍要正常抛出业务错误', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
351
+ var doc, ctx;
352
+ return tslib_1.__generator(this, function (_a) {
353
+ switch (_a.label) {
354
+ case 0:
355
+ doc = baseDoc({ approval_type: 'departmental_review' });
356
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
357
+ ctx.getObject = jest.fn(function (name) {
358
+ if (name === 'pepsico_reviewer')
359
+ return { directFind: jest.fn().mockResolvedValue([]) };
360
+ return { directInsert: jest.fn().mockRejectedValue(new Error('日志写入失败')) };
361
+ });
362
+ return [4, expect(run(ctx)).rejects.toThrow(/缺少NS、Legal、SRA、CA审核人/)];
363
+ case 1:
364
+ _a.sent();
365
+ return [2];
366
+ }
367
+ });
368
+ }); });
369
+ it('保留原有的内部协作者同步行为', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
370
+ var doc, ctx, result;
371
+ return tslib_1.__generator(this, function (_a) {
372
+ switch (_a.label) {
373
+ case 0:
374
+ doc = baseDoc({
375
+ approval_type: 'departmental_review',
376
+ ns_reviewer: ['a'], legal_reviewer: ['b'], sra_reviewer: ['c'], ca_reviewer: ['d'],
377
+ internal_collaborator_edit: 'collab-u'
378
+ });
379
+ ctx = buildCtx({ doc: doc, matrixRows: [] }).ctx;
380
+ return [4, run(ctx)];
381
+ case 1:
382
+ result = _a.sent();
383
+ expect(result).toEqual({ doc: { internal_collaborator: 'collab-u' } });
384
+ return [2];
385
+ }
386
+ });
387
+ }); });
388
+ });
389
+ //# sourceMappingURL=approvalReviewerTrigger.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvalReviewerTrigger.test.js","sourceRoot":"","sources":["../../../src/methods/__tests__/approvalReviewerTrigger.test.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,24 @@
1
1
  name: pls_file
2
2
  amis:
3
3
  id: 'u:be441c91bd73'
4
+ # 覆盖平台默认的 file 只读渲染。平台默认取 ${pls_file|asArray} 的 value/name,
5
+ # 该值只有列表视图的响应适配器(getTableApi)会重写成对象数组; lookup 弹窗(picker)的
6
+ # 适配器没有这段重写, 字段值仍是裸文件 id 字符串, 导致 value 取空 ->
7
+ # 点击后打开 cfs_files_filerecord 记录抽屉时报"无法找到记录"。
8
+ # 这里统一改为从 _display 取值(列表、lookup 弹窗、记录详情三处都有 _display),
9
+ # 并去掉 url 上的 download 参数, 让 PDF 在新标签页内联预览而不是直接下载。
10
+ body:
11
+ type: each
12
+ source: '${_display.pls_file|asArray}'
13
+ className: flex flex-col
14
+ items:
15
+ type: tpl
16
+ tpl: >-
17
+ <% if(data && data.url){ %><a href="<%= data.url.split('?')[0] %>"
18
+ target="_blank"><%= data.name %></a><% } %>
4
19
  group: 上传信息
5
20
  is_wide: true
6
- label: PLS原始文件查看
21
+ label: PLS文件
7
22
  readonly: true
8
23
  required: false
9
24
  sort_no: 150
@@ -1,6 +1,8 @@
1
1
  name: lookup
2
2
  columns:
3
- - field: name
3
+ - field: pls_file
4
+ width: '300'
5
+ wrap: true
4
6
  - field: product_name
5
7
  - field: brand
6
8
  - field: issue_date
@@ -6,30 +6,140 @@ when:
6
6
  isEnabled: true
7
7
  handler: |-
8
8
  const { _ } = global;
9
- const { doc } = ctx.params;
10
- if(ctx.params.isInsert){
11
- if(doc.approval_type === 'personnel_review'){
12
- if(_.isEmpty(doc.reviewer)){
13
- throw new Error('固定审核人缺失, 请确认审批单对应审核人员信息');
14
- }
9
+ const { doc, previousDoc } = ctx.params;
10
+
11
+ // 保存审批单时的审核人兜底与校验 (#450)
12
+ //
13
+ // 审核人原本只由 amis 字段的 onEvent.change 链回填, 服务端没有任何补偿。
14
+ // 前端链一断(链首 ajax 是 ignoreError:false、owner_department 没有 change 事件、
15
+ // 矩阵查不到时 adaptor 返回 {} 把已有值抹掉), 单子就带着空审核人存进来,
16
+ // 一路走到部门审核才因取不到审核人而中断, 静默卡死且无告警。
17
+ //
18
+ // 这里在保存这一刻就按角色矩阵补齐, 补不上就拦下 —— 空审核人的单子根本不该存进库。
19
+ //
20
+ // 只管草稿阶段: 审批过程中的修改、退回给提交人修改等都不该被拦,
21
+ // 那时单子早已在流程里, 再拦只会挡住正常操作。
22
+
23
+ // beforeUpdate 的 doc 只含本次变更字段, 必须和 previousDoc 合并后再判,
24
+ // 否则任何不碰审核人的更新(改个备注)都会被误判成"审核人为空"而拦死。
25
+ const record = _.assign({}, previousDoc || {}, doc);
26
+
27
+ const isBlank = (v) => _.isArray(v) ? _.isEmpty(_.compact(v)) : _.isEmpty(v);
28
+ // tree-select 在不同配置下可能给出字符串或单元素数组, 查矩阵时统一成字符串
29
+ const matrixKey = (v) => _.isArray(v) ? _.compact(v)[0] : v;
30
+
31
+ // 与 src/const.ts 一致: DCO 分类跳过审核直接归档, 不需要审核人
32
+ const dcoTypes = _.split(
33
+ (typeof process !== 'undefined' && process.env && process.env.CONTENT_DCO_TYPES) || 'MD-PO1-PO18',
34
+ ','
35
+ );
36
+
37
+ // 新建的单子一律是草稿(init 触发器在 beforeInsert 无条件写 current_step=draft),
38
+ // 直接认 isInsert 而不读 doc.current_step —— 两个 beforeInsert 触发器的执行顺序没有保证。
39
+ const isDraft = ctx.params.isInsert === true || record.current_step === 'draft';
40
+
41
+ const needsReviewers = isDraft
42
+ && record.review_status !== 'terminated'
43
+ && !_.includes(dcoTypes, record.classification);
44
+
45
+ if(needsReviewers){
46
+ // 角色矩阵(pepsico_reviewer)与审批单上同名的字段。显式列出是为了圈定范围,
47
+ // 避免把矩阵行上的其它字段(name / org / remark 等)一起写进审批单。
48
+ const MATRIX_FIELDS = [
49
+ 'reviewer', 'assistance',
50
+ 'ns_reviewer', 'ns_assistance',
51
+ 'legal_reviewer', 'legal_assistance',
52
+ 'sra_reviewer', 'sra_assistance',
53
+ 'ca_reviewer', 'ca_assistance',
54
+ 'qc_reviewer', 'qc_assistance',
55
+ 'can_pass_users',
56
+ 'pre_reviewer', 'pre_ns_reviewer', 'pre_legal_reviewer', 'pre_sra_reviewer', 'pre_ca_reviewer', 'pre_qc_reviewer'
57
+ ];
58
+
59
+ const classification = matrixKey(record.classification);
60
+ const product = matrixKey(record.product);
61
+ const org = matrixKey(record.owner_department);
62
+
63
+ let row = null;
64
+ if(classification && product && org){
65
+ // 矩阵上这三个字段都是 multiple, 审批单上是单值, 所以 '=' 即"矩阵行包含该值"。
66
+ // 用 directFind 绕权限: 发起人通常没有角色矩阵的读权限, 走 find 会读不到而误判成"未配置"。
67
+ const rows = await ctx.getObject('pepsico_reviewer').directFind({
68
+ filters: [
69
+ ['classification', '=', classification],
70
+ ['product', '=', product],
71
+ ['org', '=', org],
72
+ ['space', '=', record.space || ctx.params.spaceId]
73
+ ],
74
+ sort: 'modified desc',
75
+ top: 1
76
+ });
77
+ row = rows && rows.length > 0 ? rows[0] : null;
15
78
  }
16
79
 
17
- if(doc.approval_type === 'departmental_review'){
18
- if(_.isEmpty(doc.ca_reviewer)){
19
- throw new Error('CA审核人缺失, 请确认审批单对应审核人员信息');
20
- }
21
- if(_.isEmpty(doc.legal_reviewer)){
22
- throw new Error('Legal审核人缺失, 请确认审批单对应审核人员信息');
23
- }
24
- if(_.isEmpty(doc.ns_reviewer)){
25
- throw new Error('NS审核人缺失, 请确认审批单对应审核人员信息');
26
- }
27
- if(_.isEmpty(doc.sra_reviewer)){
28
- throw new Error('SRA审核人缺失, 请确认审批单对应审核人员信息');
80
+ if(row){
81
+ // 只补空, 不覆盖 —— 加急换人、管理员手工调整过的值必须保住, 覆盖会伤存量数据
82
+ _.each(MATRIX_FIELDS, (field) => {
83
+ if(isBlank(record[field]) && !isBlank(row[field])){
84
+ doc[field] = row[field];
85
+ record[field] = row[field];
86
+ }
87
+ });
88
+ }
89
+
90
+ const deptLabels = {
91
+ ns_reviewer: 'NS',
92
+ legal_reviewer: 'Legal',
93
+ sra_reviewer: 'SRA',
94
+ ca_reviewer: 'CA'
95
+ };
96
+ if(record.ec_has_detail_page === 'yes'){
97
+ deptLabels.qc_reviewer = 'QC';
98
+ }
99
+ const missingDepts = _.filter(_.keys(deptLabels), (field) => isBlank(record[field]));
100
+ const hasPersonnel = !isBlank(record.reviewer);
101
+
102
+ let missing = [];
103
+ if(record.approval_type === 'personnel_review'){
104
+ if(!hasPersonnel){
105
+ missing = ['固定'];
29
106
  }
30
- if(doc.ec_has_detail_page === 'yes' && _.isEmpty(doc.qc_reviewer)){
31
- throw new Error('QC审核人缺失, 请确认审批单对应审核人员信息');
107
+ }else if(record.approval_type === 'departmental_review'){
108
+ missing = _.map(missingDepts, (field) => deptLabels[field]);
109
+ }else if(!hasPersonnel && missingDepts.length > 0){
110
+ // approval_type 为空恰恰是最危险的情况: 它和审核人来自同一条前端链, 链断则两者同时为空。
111
+ // 旧逻辑在这里直接跳过校验, 空审核人的单子就是这样进来的, 所以不能以它有值为前提。
112
+ // 此时无法确定后续会走哪条分支(approval_type 由服务端在提交时重算),
113
+ // 要求至少有一套可用的审核人: 固定人员, 或四部门齐全。
114
+ missing = _.map(missingDepts, (field) => deptLabels[field]);
115
+ }
116
+
117
+ if(missing.length > 0){
118
+ // 原始 ID 写系统日志给管理员定位; 发起人就在表单上, 分类/品牌/部门他看得见, 不必回显
119
+ try {
120
+ await ctx.getObject('logs').directInsert({
121
+ name: '保存被拦截: 审核人缺失',
122
+ level: 'error',
123
+ data: JSON.stringify({
124
+ action: 'save-blocked-reviewer-missing',
125
+ id: record._id || ctx.params.id,
126
+ approval_name: record.name,
127
+ is_insert: ctx.params.isInsert === true,
128
+ missing: missing,
129
+ matched_pepsico_reviewer: row ? row._id : null,
130
+ approval_type: record.approval_type || null,
131
+ classification: classification,
132
+ product: product,
133
+ owner_department: org
134
+ }),
135
+ space: record.space || ctx.params.spaceId,
136
+ created: new Date()
137
+ });
138
+ } catch (e) {
139
+ console.error('[pepsico_material_approval_change] 写系统日志失败', e && e.message);
32
140
  }
141
+
142
+ throw new Error('缺少' + missing.join('、') + '审核人, 请联系管理员为当前「分类 + 品牌/产品 + 发起人部门」的组合配置角色矩阵后重试。');
33
143
  }
34
144
  }
35
145
 
@@ -40,7 +150,7 @@ handler: |-
40
150
  }
41
151
  }
42
152
  }
43
-
153
+
44
154
  authentication_type: none
45
155
  locked: false
46
156
  type: code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steedos-labs/content-compliance",
3
- "version": "0.8.26",
3
+ "version": "0.8.27",
4
4
  "description": "内容审核系统",
5
5
  "main": "package.service.js",
6
6
  "scripts": {