Commit 6451538611905ae8436d2bb680c5045faa92c3fa

Authored by YunaiV
1 parent 00680ddc

test(oa): restore BPM model initializer

yudao-module-oa/src/test/java/cn/iocoder/yudao/module/oa/service/OaBpmModelInitTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.oa.service;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.io.resource.ResourceUtil;
  5 +import cn.hutool.http.HttpResponse;
  6 +import cn.hutool.http.HttpUtil;
  7 +import cn.hutool.json.JSONObject;
  8 +import cn.hutool.json.JSONUtil;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.junit.jupiter.api.Disabled;
  11 +import org.junit.jupiter.api.Test;
  12 +
  13 +import java.nio.charset.StandardCharsets;
  14 +import java.util.Collections;
  15 +
  16 +import static org.junit.jupiter.api.Assertions.*;
  17 +
  18 +/**
  19 + * OA 流程初始化测试类
  20 + *
  21 + * 用于手动初始化 OA 业务所需的 BPM 流程模型,不用于业务流程提交或审批测试。
  22 + * 读取 main/resources/bpm 中的流程文件,通过 HTTP 接口导入并发布。
  23 + * 已存在的模型不覆盖,未发布时补充发布,已发布时不重复创建流程版本。
  24 + *
  25 + * @author 芋道源码
  26 + */
  27 +@Disabled("手动初始化流程模型时临时移除,需启动本地后端并开启 mock 登录")
  28 +@Slf4j
  29 +public class OaBpmModelInitTest {
  30 +
  31 + private static final String BASE_URL = "http://127.0.0.1:48080/admin-api";
  32 + private static final Long TENANT_ID = 1L;
  33 + private static final Long USER_ID = 1L;
  34 +
  35 + private static final String TOKEN = "test" + USER_ID; // 本地默认 mockSecret 为 test,后缀为模拟用户编号,不是真实登录令牌
  36 +
  37 + /**
  38 + * 初始化用车申请流程
  39 + */
  40 + @Test
  41 + public void testInitVehicleApplyModel() {
  42 + // 调用,并断言流程已发布
  43 + initModel("oa_vehicle_apply.json");
  44 + }
  45 +
  46 + /**
  47 + * 初始化还车申请流程
  48 + */
  49 + @Test
  50 + public void testInitVehicleReturnModel() {
  51 + // 调用,并断言流程已发布
  52 + initModel("oa_vehicle_return.json");
  53 + }
  54 +
  55 + /**
  56 + * 初始化会议室预定流程
  57 + */
  58 + @Test
  59 + public void testInitMeetingRoomBookingModel() {
  60 + // 调用,并断言流程已发布
  61 + initModel("oa_meeting_room_booking.json");
  62 + }
  63 +
  64 + /**
  65 + * 初始化费用报销流程
  66 + */
  67 + @Test
  68 + public void testInitReimbursementModel() {
  69 + // 调用,并断言流程已发布
  70 + initModel("oa_reimbursement.json");
  71 + }
  72 +
  73 + /**
  74 + * 初始化加班申请流程
  75 + */
  76 + @Test
  77 + public void testInitOvertimeApplyModel() {
  78 + // 调用,并断言流程已发布
  79 + initModel("oa_overtime_apply.json");
  80 + }
  81 +
  82 + /**
  83 + * 初始化请假申请流程
  84 + */
  85 + @Test
  86 + public void testInitLeaveApplyModel() {
  87 + // 调用,并断言流程已发布
  88 + initModel("oa_leave_apply.json");
  89 + }
  90 +
  91 + /**
  92 + * 初始化转正申请流程
  93 + */
  94 + @Test
  95 + public void testInitRegularApplyModel() {
  96 + // 调用,并断言流程已发布
  97 + initModel("oa_regular_apply.json");
  98 + }
  99 +
  100 + /**
  101 + * 初始化离职申请流程
  102 + */
  103 + @Test
  104 + public void testInitResignApplyModel() {
  105 + // 调用,并断言流程已发布
  106 + initModel("oa_resign_apply.json");
  107 + }
  108 +
  109 + /**
  110 + * 初始化用品领用申请流程
  111 + */
  112 + @Test
  113 + public void testInitSupplyApplyModel() {
  114 + // 调用,并断言流程已发布
  115 + initModel("oa_supply_apply.json");
  116 + }
  117 +
  118 + /**
  119 + * 初始化出差申请流程
  120 + */
  121 + @Test
  122 + public void testInitTravelApplyModel() {
  123 + // 调用,并断言流程已发布
  124 + initModel("oa_travel_apply.json");
  125 + }
  126 +
  127 + /**
  128 + * 初始化出差报销流程
  129 + */
  130 + @Test
  131 + public void testInitTravelReimbursementModel() {
  132 + // 调用,并断言流程已发布
  133 + initModel("oa_travel_reimbursement.json");
  134 + }
  135 +
  136 + /**
  137 + * 初始化用印申请流程
  138 + */
  139 + @Test
  140 + public void testInitSealApplyModel() {
  141 + // 调用,并断言流程已发布
  142 + initModel("oa_seal_apply.json");
  143 + }
  144 +
  145 + /**
  146 + * 初始化公文发文流程
  147 + */
  148 + @Test
  149 + public void testInitOfficialDocSendModel() {
  150 + // 调用,并断言流程已发布
  151 + initModel("oa_official_doc_send.json");
  152 + }
  153 +
  154 + /**
  155 + * 初始化公文收文流程
  156 + */
  157 + @Test
  158 + public void testInitOfficialDocReceiveModel() {
  159 + // 调用,并断言流程已发布
  160 + initModel("oa_official_doc_receive.json");
  161 + }
  162 +
  163 + /**
  164 + * 初始化流程模型
  165 + *
  166 + * @param fileName bpm 资源目录下的流程文件名
  167 + */
  168 + private void initModel(String fileName) {
  169 + try {
  170 + // 1. 准备模型参数
  171 + JSONObject model = JSONUtil.parseObj(ResourceUtil.readUtf8Str("bpm/" + fileName));
  172 + model.set("managerUserIds", Collections.singletonList(USER_ID));
  173 +
  174 + // 2. 查询已有模型,不覆盖本地已调整的流程
  175 + JSONObject existingModel;
  176 + try (HttpResponse response = HttpUtil.createGet(BASE_URL + "/bpm/model/list")
  177 + .header("Authorization", "Bearer " + TOKEN)
  178 + .header("tenant-id", TENANT_ID.toString())
  179 + .timeout(30000).execute()) {
  180 + assertEquals(200, response.getStatus());
  181 + JSONObject result = JSONUtil.parseObj(response.body());
  182 + assertEquals(Integer.valueOf(0), result.getInt("code"), result.getStr("msg"));
  183 + existingModel = CollUtil.findOne(result.getJSONArray("data").toList(JSONObject.class),
  184 + item -> model.getStr("key").equals(item.getStr("key")));
  185 + }
  186 +
  187 + // 3. 导入不存在的模型,补充发布尚未发布的模型
  188 + String modelId = existingModel != null ? existingModel.getStr("id") : importModel(fileName, model);
  189 + if (existingModel == null || existingModel.getJSONObject("processDefinition") == null) {
  190 + deployModel(modelId);
  191 + }
  192 +
  193 + // 4. 断言流程定义已生效,可用于业务提交
  194 + try (HttpResponse response = HttpUtil.createGet(BASE_URL + "/bpm/process-definition/get")
  195 + .header("Authorization", "Bearer " + TOKEN)
  196 + .header("tenant-id", TENANT_ID.toString())
  197 + .form("key", model.getStr("key"))
  198 + .timeout(30000).execute()) {
  199 + assertEquals(200, response.getStatus());
  200 + JSONObject result = JSONUtil.parseObj(response.body());
  201 + assertEquals(Integer.valueOf(0), result.getInt("code"), result.getStr("msg"));
  202 + JSONObject definition = result.getJSONObject("data");
  203 + assertNotNull(definition, "未找到生效的流程定义,请检查模型是否已发布或被挂起");
  204 + assertEquals(model.getStr("key"), definition.getStr("key"));
  205 + assertNotNull(definition.getStr("id"));
  206 + log.info("[initModel][流程初始化成功,文件:{},模型编号:{},流程定义编号:{}]",
  207 + fileName, modelId, definition.getStr("id"));
  208 + }
  209 + } catch (RuntimeException | AssertionError ex) {
  210 + log.error("[initModel][流程初始化失败,文件:{}]", fileName, ex);
  211 + throw ex;
  212 + }
  213 + }
  214 +
  215 + /**
  216 + * 导入流程模型
  217 + *
  218 + * @param fileName 流程文件名
  219 + * @param model 流程模型
  220 + * @return 模型编号
  221 + */
  222 + private String importModel(String fileName, JSONObject model) {
  223 + try (HttpResponse response = HttpUtil.createPost(BASE_URL + "/bpm/model/import")
  224 + .header("Authorization", "Bearer " + TOKEN)
  225 + .header("tenant-id", TENANT_ID.toString())
  226 + .form("file", model.toString().getBytes(StandardCharsets.UTF_8), fileName)
  227 + .timeout(30000).execute()) {
  228 + assertEquals(200, response.getStatus());
  229 + JSONObject result = JSONUtil.parseObj(response.body());
  230 + assertEquals(Integer.valueOf(0), result.getInt("code"), result.getStr("msg"));
  231 + assertNotNull(result.getStr("data"));
  232 + return result.getStr("data");
  233 + }
  234 + }
  235 +
  236 + /**
  237 + * 发布流程模型
  238 + *
  239 + * @param modelId 模型编号
  240 + */
  241 + private void deployModel(String modelId) {
  242 + try (HttpResponse response = HttpUtil.createPost(BASE_URL + "/bpm/model/deploy")
  243 + .header("Authorization", "Bearer " + TOKEN)
  244 + .header("tenant-id", TENANT_ID.toString())
  245 + .form("id", modelId)
  246 + .timeout(30000).execute()) {
  247 + assertEquals(200, response.getStatus());
  248 + JSONObject result = JSONUtil.parseObj(response.body());
  249 + assertEquals(Integer.valueOf(0), result.getInt("code"), result.getStr("msg"));
  250 + assertEquals(Boolean.TRUE, result.getBool("data"));
  251 + }
  252 + }
  253 +}