刘光辉
11 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package jnpf.onlinedev.controller;
 
import jnpf.base.ActionResult;
import jnpf.base.DataSourceApi;
import jnpf.base.entity.VisualAliasEntity;
import jnpf.base.entity.VisualdevEntity;
import jnpf.base.entity.VisualdevReleaseEntity;
import jnpf.base.model.FormDeleteResult;
import jnpf.base.model.VisualDevJsonModel;
import jnpf.base.model.export.VisualExportVo;
import jnpf.base.model.flow.DataModel;
import jnpf.base.model.flow.FlowFormDataModel;
import jnpf.base.model.flow.FlowStateModel;
import jnpf.base.model.online.VisualdevModelDataInfoVO;
import jnpf.base.service.FlowFormRelationService;
import jnpf.base.service.VisualAliasService;
import jnpf.base.service.VisualdevReleaseService;
import jnpf.base.service.VisualdevService;
import jnpf.base.util.FlowFormDataUtil;
import jnpf.base.util.FlowFormHttpReqUtils;
import jnpf.database.model.entity.DbLinkEntity;
import jnpf.onlinedev.VisualdevOnlineApi;
import jnpf.onlinedev.model.OnlineInfoModel;
import jnpf.onlinedev.model.VisualParamModel;
import jnpf.onlinedev.model.log.VisualLogForm;
import jnpf.onlinedev.service.VisualDevInfoService;
import jnpf.onlinedev.service.VisualDevListService;
import jnpf.onlinedev.service.VisualLogService;
import jnpf.onlinedev.service.VisualdevModelDataService;
import jnpf.onlinedev.util.FlowFormCustomUtils;
import jnpf.onlinedev.util.OnlinePublicUtils;
import jnpf.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/VisualdevForFlow")
public class VisualdevForFlowController implements VisualdevOnlineApi {
 
    private final VisualdevReleaseService visualdevReleaseService;
    private final VisualDevListService visualDevListService;
    private final VisualDevInfoService visualDevInfoService;
    private final FlowFormHttpReqUtils flowFormHttpReqUtils;
    private final FlowFormCustomUtils flowFormCustomUtils;
    private final FlowFormDataUtil flowDataUtil;
    private final DataSourceApi dblinkService;
    private final FlowFormRelationService flowFormRelationService;
    private final VisualdevModelDataService visualdevModelDataService;
    private final VisualdevService visualdevService;
    private final VisualAliasService aliasService;
    // AUDIT 补丁(2026-08-05,登记 tasks/jnpf-platform-patches.md #7):
    // 流程实例删除会连带删掉业务表单数据,平台原生此路径不发任何 VSLOG 事件。
    private final VisualLogService visualLogService;
 
    @Override
    @PostMapping("/saveOrUpdate")
    public ActionResult<Object> saveOrUpdate(@RequestBody FlowFormDataModel flowFormDataModel) {
        DataModel dataModel = null;
        try {
            String id = flowFormDataModel.getId();
            String formId = flowFormDataModel.getFormId();
            String flowId = flowFormDataModel.getFlowId();
            Map<String, Object> map = flowFormDataModel.getMap();
            List<Map<String, Object>> formOperates = flowFormDataModel.getFormOperates();
            VisualdevReleaseEntity entity = visualdevReleaseService.getById(formId);
            VisualdevEntity visualdevEntity = JsonUtil.getJsonToBean(entity, VisualdevEntity.class);
 
            map.put(FlowFormConstant.FLOWID, flowId);
            if (map.get(TableFeildsEnum.VERSION.getField().toUpperCase()) != null) {//针对Oracle数据库大小写敏感,出现大写字段补充修复
                map.put(TableFeildsEnum.VERSION.getField(), map.get(TableFeildsEnum.VERSION.getField().toUpperCase()));
            }
 
            //系统表单
            if (entity.getType() == 2) {
                map.put("formOperates", formOperates);
                flowFormHttpReqUtils.saveOrUpdate(visualdevEntity, id, UserProvider.getToken(), map);
            } else {
                dataModel = flowFormCustomUtils.saveOrUpdate(visualdevEntity, flowFormDataModel);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ActionResult.fail(e.getMessage());
        }
        return ActionResult.success(dataModel);
    }
 
    @Override
    @DeleteMapping("/delete")
    public boolean delete(@RequestParam("formId") String formId, @RequestParam("id") String id) {
        try {
            VisualdevReleaseEntity entity = visualdevReleaseService.getById(formId);
            VisualdevEntity visualdevEntity = JsonUtil.getJsonToBean(entity, VisualdevEntity.class);
            //系统表单
            if (entity.getType() == 2) {
                // 系统表单的数据不在本库(走 HTTP 打到外部系统),本地无快照可取,
                // 也不该产出指向本库的审计事件——这条分支不发。
                flowFormHttpReqUtils.delete(visualdevEntity, id, UserProvider.getToken());
            } else {
                VisualDevJsonModel visualJsonModel = OnlinePublicUtils.getVisualJsonModel(visualdevEntity);
                DbLinkEntity linkEntity = StringUtil.isNotEmpty(visualdevEntity.getDbLinkId()) ? dblinkService.getInfo(visualdevEntity.getDbLinkId()) : null;
 
                // ── AUDIT 补丁(2026-08-05,登记 tasks/jnpf-platform-patches.md #7)第 1/2 段 ──
                // 触发链:用户在流程列表删除流程实例 → TaskServiceImpl.delete/deleteBatch
                //        → OperatorUtil.deleteFormData → ServiceUtil.deleteFormData
                //        → Feign visualdevApi.delete(formId, id) → 本方法。
                // 🔴 顺序是硬约束:主键解析与快照都必须在 deleteTable **之前**——删完再查什么都没有。
                // 入参 id 可能是 flowTaskId(deleteTable 按「主键 = id OR flowTaskId = id」定位),
                // 而审计事件的 dataId 必须是业务主键,否则查询侧对不上记录。
                Object auditMainId = null;
                Map<String, Object> auditSnapshot = null;
                try {
                    auditMainId = flowDataUtil.resolveMainId(id, visualJsonModel, linkEntity);
                    if (auditMainId != null) {
                        auditSnapshot = auditDeleteSnapshot(String.valueOf(auditMainId), visualdevEntity);
                    }
                } catch (Throwable t) {
                    log.warn("[AUDIT] 流程删除取快照失败,本次删除不带内容快照。formId={}, id={}, error={}",
                            formId, id, t.getMessage());
                    auditMainId = null;
                    auditSnapshot = null;
                }
                // ── 第 1/2 段结束 ──────────────────────────────────────────────────────────
 
                FormDeleteResult delResult = flowDataUtil.deleteTable(id, visualJsonModel, linkEntity);
 
                // ── AUDIT 补丁 第 2/2 段 ───────────────────────────────────────────────────
                // dataId 回落到 delResult.getMainId():上面解析失败但删除成功时,
                // 「这次删过」这个事实不能缺——缺的只能是内容快照。
                // 不设 batchOpId/batchSize:这是单条删除,审计侧 batchOpId 为空时回落到
                // EVENT_ID_PREFIX + eventId,batchSize 为 null 时不缀「共 N 条」。
                // 不设 bizSign:流程实例删除没有电子签名入口。
                Object eventDataId = auditMainId != null ? auditMainId : (delResult == null ? null : delResult.getMainId());
                if (delResult != null && delResult.isDeleted() && eventDataId != null) {
                    try {
                        visualLogService.createEventLog(VisualLogForm.builder()
                                .modelId(formId)
                                .dataId(String.valueOf(eventDataId))
                                .newData(auditSnapshot)
                                .type(2)
                                .build());
                    } catch (Throwable t) {
                        log.warn("[AUDIT] 流程删除事件发布失败,已跳过。formId={}, id={}, error={}",
                                formId, id, t.getMessage());
                    }
                }
                // ── AUDIT 补丁结束 ────────────────────────────────────────────────────────
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }
 
    @Override
    @GetMapping("/info")
    public ActionResult<Object> info(@RequestParam("formId") String formId, @RequestParam("id") String id) {
        ActionResult<Object> result = new ActionResult<>();
        Map<String, Object> allDataMap = new HashMap<>();
        VisualdevReleaseEntity entity = visualdevReleaseService.getById(formId);
        VisualdevEntity visualdevEntity = JsonUtil.getJsonToBean(entity, VisualdevEntity.class);
        result.setCode(visualdevEntity == null ? 400 : 200);
        result.setMsg(visualdevEntity == null ? "表单信息不存在" : "");
        if (visualdevEntity != null) {
            //判断是否为系统表单
            boolean b = visualdevEntity.getType() == 2;
            if (b) {
                allDataMap.putAll(flowFormHttpReqUtils.info(visualdevEntity, id, UserProvider.getToken()));
            } else {
                allDataMap.putAll(flowFormCustomUtils.info(visualdevEntity, id));
            }
        }
        result.setData(allDataMap);
        return result;
    }
 
    @Override
    @GetMapping("/getFormConfig")
    public VisualdevEntity getFormConfig(@RequestParam("formId") String formId) {
        VisualdevReleaseEntity entity = visualdevReleaseService.getById(formId);
        return JsonUtil.getJsonToBean(entity, VisualdevEntity.class);
    }
 
    @Override
    @PostMapping("/getFormConfigList")
    public List<VisualdevEntity> getFormConfigList(@RequestBody List<String> formIds) {
        List<VisualdevReleaseEntity> list = visualdevReleaseService.selectByIds(formIds,
                VisualdevReleaseEntity::getId,
                VisualdevReleaseEntity::getEnCode,
                VisualdevReleaseEntity::getFullName,
                VisualdevReleaseEntity::getWebType,
                VisualdevReleaseEntity::getType);
        return CollectionUtils.isNotEmpty(list) ? JsonUtil.getJsonToList(list, VisualdevEntity.class) : new ArrayList<>();
    }
 
    @Override
    @PostMapping("/saveFlowIdByFormIds")
    public void saveFlowIdByFormIds(@RequestBody String flowId, @RequestParam("formId") List<String> formIds) {
        flowFormRelationService.saveFlowIdByFormIds(flowId, formIds);
    }
 
    @Override
    @GetMapping("/getReleaseInfo")
    public VisualdevEntity getReleaseInfo(@RequestParam("formId") String formId) {
        return visualdevService.getReleaseInfo(formId);
    }
 
    @Override
    @PostMapping("/getListWithTableList")
    public List<Map<String, Object>> getListWithTableList(@RequestBody VisualParamModel model) {
        return visualDevListService.getListWithTableList(model.getVisualDevJsonModel(), model.getPagination(), model.getUserInfo());
    }
 
    @Override
    @PostMapping("/getEditDataInfo")
    public VisualdevModelDataInfoVO getEditDataInfo(@RequestBody VisualParamModel model) {
        return visualDevInfoService.getEditDataInfo(model.getId(), model.getVisualdevEntity(), OnlineInfoModel.builder().build());
    }
 
    @Override
    @PostMapping("/visualCreate")
    public ActionResult<Object> visualCreate(@RequestBody VisualParamModel model) {
        DataModel dataModel;
        try {
            dataModel = visualdevModelDataService.visualCreate(VisualParamModel.builder().visualdevEntity(model.getVisualdevEntity()).data(model.getData()).build());
        } catch (Exception e) {
            e.printStackTrace();
            return ActionResult.fail(e.getMessage());
        }
        return ActionResult.success(dataModel);
    }
 
    @Override
    @PostMapping("/visualUpdate")
    public ActionResult<Object> visualUpdate(@RequestBody VisualParamModel model) {
        DataModel dataModel;
        try {
            dataModel = visualdevModelDataService.visualUpdate(model);
        } catch (Exception e) {
            e.printStackTrace();
            return ActionResult.fail(e.getMessage());
        }
        return ActionResult.success(dataModel);
    }
 
    @Override
    @PostMapping("/visualDelete")
    public ActionResult<Object> visualDelete(@RequestBody VisualParamModel model) {
        try {
            visualdevModelDataService.visualDelete(model.getVisualdevEntity(), model.getDataList());
        } catch (Exception e) {
            e.printStackTrace();
            return ActionResult.fail(e.getMessage());
        }
        return ActionResult.success();
    }
 
    @Override
    @PostMapping("/deleteByTableName")
    public ActionResult<Object> deleteByTableName(@RequestBody FlowFormDataModel model) {
        try {
            visualdevModelDataService.deleteByTableName(model);
        } catch (Exception e) {
            e.printStackTrace();
            return ActionResult.fail(e.getMessage());
        }
        return ActionResult.success();
    }
 
    @Override
    @PostMapping("/saveState")
    public void saveState(@RequestBody FlowStateModel model) {
        List<String> formIds = model.getFormIds();
        String flowTaskId = model.getFlowTaskId();
        Integer flowState = model.getFlowState();
        for (String formId : formIds) {
            VisualdevReleaseEntity entity = visualdevReleaseService.getById(formId);
            if (entity == null) continue;
            VisualdevEntity visualdevEntity = JsonUtil.getJsonToBean(entity, VisualdevEntity.class);
            //系统表单
            if (entity.getType() == 2) {
                flowFormHttpReqUtils.saveState(visualdevEntity, flowTaskId, flowState);
            } else {
                flowDataUtil.saveState(visualdevEntity, flowTaskId, flowState);
            }
        }
    }
 
    @Override
    @GetMapping("/getExportList")
    public List<VisualExportVo> getExportList(@RequestParam("systemId") String systemId) {
        return visualdevService.getExportList(systemId);
    }
 
    @Override
    @PostMapping("/importCopy")
    public boolean importCopy(@RequestBody List<VisualExportVo> list, @RequestParam("systemId") String systemId) {
        try {
            for (VisualExportVo item : list) {
                VisualdevEntity entity = JsonUtil.getJsonToBean(item, VisualdevEntity.class);
                String id = RandomUtil.uuId();
                entity.setId(id);
                entity.setCreatorTime(DateUtil.getNowDate());
                entity.setCreatorUserId(UserProvider.getUser().getUserId());
                entity.setLastModifyTime(null);
                entity.setLastModifyUserId(null);
                entity.setState(0);
                entity.setSystemId(systemId);
                visualdevService.setAutoEnCode(entity);
                visualdevService.save(entity);
                if (StringUtil.isNotEmpty(item.getAliasListJson())) {
                    List<VisualAliasEntity> jsonToList = JsonUtil.getJsonToList(item.getAliasListJson(), VisualAliasEntity.class);
                    for (VisualAliasEntity aliasEntity : jsonToList) {
                        aliasService.copyEntity(aliasEntity, id);
                    }
                }
            }
            return true;
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return false;
    }
 
    @Override
    @GetMapping("/deleteBySystemId")
    public void deleteBySystemId(@RequestParam("systemId") String systemId) {
        visualdevService.deleteBySystemId(systemId);
    }
 
    /**
     * AUDIT 补丁(2026-08-05,登记 tasks/jnpf-platform-patches.md #7):
     * 取被删数据的快照,供审计层 0 渲染标题与留存 data_snapshot。
     *
     * <p>与 {@code VisualdevModelDataController.auditDeleteSnapshot} 同款参数
     * (needRlationFiled=true, needSwap=false),保证两条删除路径产出的 newData 同构,
     * 审计侧的渲染链路得以原样复用。
     *
     * <p>入参必须是**业务主键**(不是 flowTaskId)——调用方先经
     * {@code flowDataUtil.resolveMainId} 解析。
     *
     * <p>取不到返回 null:事件照发(「这次删过」不能缺),只是没有快照可渲染。
     */
    private Map<String, Object> auditDeleteSnapshot(String id, VisualdevEntity visualdevEntity) {
        try {
            VisualdevModelDataInfoVO info = visualDevInfoService.getEditDataInfo(id, visualdevEntity,
                    OnlineInfoModel.builder().needRlationFiled(true).needSwap(false).build());
            return info == null ? null : JsonUtil.stringToMap(info.getData());
        } catch (Throwable t) {
            log.warn("[AUDIT] 取删除快照失败 id={}, error={}", id, t.getMessage());
            return null;
        }
    }
 
}