package jnpf.dmsService.impl;
|
|
import jnpf.base.ActionResult;
|
import jnpf.base.BillRuleApi;
|
import jnpf.base.UserInfo;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import jnpf.audit.sdk.annotation.AuditLog;
|
import jnpf.dms.DmsResourceApi;
|
import jnpf.dmsEntity.DmsWenjianBanbenEntity;
|
import jnpf.dmsEntity.permission.form.DmsPersonalFolderFileForm;
|
import jnpf.dmsEntity.print.form.DmsWorkflowPrintFilesGrantForm;
|
import jnpf.dmsEntity.print.vo.DmsWorkflowPrintFilesGrantResultVO;
|
import jnpf.dmsEntity.preview.vo.DmsFilePageCountVO;
|
import jnpf.dmsEntity.recordprint.DmsJiluDayinLiushuiEntity;
|
import jnpf.dmsEntity.recordprint.DmsJiluDayinShenqingEntity;
|
import jnpf.dmsEntity.recordprint.DmsJiluDayinShenqingZixiangEntity;
|
import jnpf.dmsEntity.recordprint.form.DmsRecordPrintHuishouForm;
|
import jnpf.dmsEntity.recordprint.form.DmsRecordPrintHuishouItemForm;
|
import jnpf.dmsEntity.recordprint.form.DmsRecordPrintIssueForm;
|
import jnpf.dmsEntity.recordprint.form.DmsRecordPrintIssueItemForm;
|
import jnpf.dmsEntity.recordprint.vo.DmsRecordPrintConfirmVO;
|
import jnpf.dmsEntity.recordprint.vo.DmsRecordPrintPrepareVO;
|
import jnpf.dmsEntity.recordprint.vo.DmsRecordPrintSerialVO;
|
import jnpf.dmsMapper.DmsRecordPrintItemMapper;
|
import jnpf.dmsMapper.DmsRecordPrintRequestMapper;
|
import jnpf.dmsMapper.DmsJiluDayinLiushuiMapper;
|
import jnpf.dmsMapper.DmsWenjianBanbenMapper;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsService.DmsFilePageCountService;
|
import jnpf.dmsService.recordprint.DmsRecordPrintPageParser;
|
import jnpf.dmsService.recordprint.DmsRecordPrintProperties;
|
import jnpf.dmsService.recordprint.DmsRecordPrintReservation;
|
import jnpf.dmsService.recordprint.DmsRecordPrintReservationStore;
|
import jnpf.exception.DataException;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
import org.apache.ibatis.session.Configuration;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
import java.util.Date;
|
import java.util.List;
|
import java.util.UUID;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.isNull;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
@ExtendWith(MockitoExtension.class)
|
class DmsRecordPrintServiceImplTest {
|
|
@Mock private DmsJiluDayinLiushuiMapper mapper;
|
@Mock private DmsRecordPrintItemMapper itemMapper;
|
@Mock private DmsRecordPrintRequestMapper requestMapper;
|
@Mock private DmsWenjianBanbenMapper banbenMapper;
|
@Mock private DmsResourceApi dmsResourceApi;
|
@Mock private DmsCurrentIdentity identity;
|
@Mock private BillRuleApi billRuleApi;
|
@Mock private DmsFilePageCountService pageCountService;
|
@Mock private DmsRecordPrintReservationStore reservationStore;
|
|
private DmsRecordPrintServiceImpl service;
|
|
@Test
|
void completesPrintItemUsingEntity() {
|
when(itemMapper.update(any(DmsJiluDayinShenqingZixiangEntity.class), any()))
|
.thenReturn(1);
|
|
service.complete(" item-1 ");
|
|
ArgumentCaptor<DmsJiluDayinShenqingZixiangEntity> item =
|
ArgumentCaptor.forClass(DmsJiluDayinShenqingZixiangEntity.class);
|
verify(itemMapper).update(item.capture(), any());
|
assertEquals("dayin_wancheng", item.getValue().getDayinZhuangtai());
|
}
|
|
@Test
|
void rejectsMissingPrintItemOnCompletion() {
|
when(itemMapper.update(any(DmsJiluDayinShenqingZixiangEntity.class), any()))
|
.thenReturn(0);
|
|
assertThrows(DataException.class, () -> service.complete("missing-item"));
|
}
|
|
/**
|
* 初始化打印服务及当前登录用户。
|
*/
|
@BeforeEach
|
void setUp() throws Exception {
|
if (TableInfoHelper.getTableInfo(DmsJiluDayinLiushuiEntity.class) == null) {
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), ""),
|
DmsJiluDayinLiushuiEntity.class);
|
}
|
if (TableInfoHelper.getTableInfo(DmsJiluDayinShenqingZixiangEntity.class) == null) {
|
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), ""),
|
DmsJiluDayinShenqingZixiangEntity.class);
|
}
|
DmsRecordPrintProperties properties = new DmsRecordPrintProperties();
|
service = new DmsRecordPrintServiceImpl(mapper, itemMapper, requestMapper,
|
banbenMapper, dmsResourceApi,
|
identity, billRuleApi,
|
pageCountService, new DmsRecordPrintPageParser(), properties,
|
reservationStore);
|
}
|
|
/**
|
* 验证流程申请中的全部业务版本会被组装为永久打印授权。
|
*/
|
@Test
|
void authorizesAllRecordPrintVersionsForApplicant() {
|
DmsJiluDayinShenqingEntity request = new DmsJiluDayinShenqingEntity();
|
request.setId("request-1");
|
request.setShenqingren(" user-1 ");
|
DmsJiluDayinShenqingZixiangEntity first = new DmsJiluDayinShenqingZixiangEntity();
|
first.setBanbenId("version-1");
|
DmsJiluDayinShenqingZixiangEntity second = new DmsJiluDayinShenqingZixiangEntity();
|
second.setBanbenId("version-2");
|
DmsWenjianBanbenEntity firstVersion = businessVersion("version-1");
|
DmsWenjianBanbenEntity secondVersion = businessVersion("version-2");
|
DmsWorkflowPrintFilesGrantResultVO expected =
|
new DmsWorkflowPrintFilesGrantResultVO(2, 2);
|
ActionResult<DmsWorkflowPrintFilesGrantResultVO> response = new ActionResult<>();
|
response.setCode(200);
|
response.setData(expected);
|
|
when(requestMapper.selectList(any())).thenReturn(List.of(request));
|
when(itemMapper.selectList(any())).thenReturn(List.of(first, second));
|
when(banbenMapper.selectList(any())).thenReturn(List.of(firstVersion, secondVersion));
|
when(dmsResourceApi.grantWorkflowPrintFiles(eq("jnpf-flowable"), any()))
|
.thenReturn(response);
|
|
DmsWorkflowPrintFilesGrantResultVO actual = service.authorize(" flow-1 ", " task-1 ");
|
|
assertSame(expected, actual);
|
ArgumentCaptor<DmsWorkflowPrintFilesGrantForm> captor =
|
ArgumentCaptor.forClass(DmsWorkflowPrintFilesGrantForm.class);
|
verify(dmsResourceApi).grantWorkflowPrintFiles(eq("jnpf-flowable"), captor.capture());
|
assertEquals("user-1", captor.getValue().getUserId());
|
assertEquals(2, captor.getValue().getFiles().size());
|
for (DmsPersonalFolderFileForm.FileRelation relation : captor.getValue().getFiles()) {
|
assertNull(relation.getValidTo());
|
}
|
}
|
|
/** 验证不存在流程申请时不会调用授权接口。 */
|
@Test
|
void rejectsAuthorizationWhenRequestDoesNotExist() {
|
when(requestMapper.selectList(any())).thenReturn(List.of());
|
|
DataException exception = assertThrows(DataException.class,
|
() -> service.authorize("flow-1", "task-1"));
|
|
assertTrue(exception.getMessage().contains("未找到记录打印申请"));
|
verify(dmsResourceApi, never()).grantWorkflowPrintFiles(any(), any());
|
}
|
|
/**
|
* 验证流水查询由 A、B 表实体组装指定的四个返回字段。
|
*/
|
@Test
|
void findsUnissuedRecordBySerialNumber() throws Exception {
|
DmsJiluDayinLiushuiEntity record = new DmsJiluDayinLiushuiEntity();
|
record.setId("record-1");
|
record.setLiushuihao("LS001");
|
record.setDayinZixiangId("item-1");
|
DmsJiluDayinShenqingZixiangEntity item = new DmsJiluDayinShenqingZixiangEntity();
|
item.setWenjianMingcheng("检验记录");
|
item.setBanbenhao("V2");
|
when(mapper.selectOne(any())).thenReturn(record);
|
when(itemMapper.selectOne(any())).thenReturn(item);
|
|
DmsRecordPrintSerialVO result = service.findBySerialNumber(" LS001 ", "");
|
|
assertEquals("LS001", result.getLiushuihao());
|
assertEquals("record-1", result.getId());
|
assertEquals("检验记录", result.getWenjianMingcheng());
|
assertEquals("V2", result.getBanbenhao());
|
}
|
|
/**
|
* 验证发放人有值时拒绝条码重复签收或发放。
|
*/
|
@Test
|
void rejectsSerialNumberWithIssuer() {
|
DmsJiluDayinLiushuiEntity record = new DmsJiluDayinLiushuiEntity();
|
record.setFafangren("user-1");
|
when(mapper.selectOne(any())).thenReturn(record);
|
|
DataException exception = assertThrows(DataException.class,
|
() -> service.findBySerialNumber("LS001", ""));
|
|
assertEquals("该条码已签收或发放请勿重复操作", exception.getMessage());
|
verify(itemMapper, never()).selectOne(any());
|
}
|
|
/**
|
* 验证批量发放使用条件更新写入双方信息和同一个操作时间。
|
*/
|
@Test
|
void issuesEveryRecordWithOneOperationTime() throws Exception {
|
DmsJiluDayinLiushuiEntity first = issueRecord("record-1", "LS001", null);
|
DmsJiluDayinLiushuiEntity second = issueRecord("record-2", "LS002", "");
|
DmsJiluDayinShenqingZixiangEntity printItem = new DmsJiluDayinShenqingZixiangEntity();
|
printItem.setDayinFenshu("5");
|
printItem.setYiqianfaShuliang("1");
|
when(mapper.selectList(any())).thenReturn(java.util.Arrays.asList(first, second));
|
when(mapper.update(isNull(), any())).thenReturn(1);
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(printItem);
|
when(itemMapper.update(any(DmsJiluDayinShenqingZixiangEntity.class), any()))
|
.thenReturn(1);
|
|
service.issue(issueForm(issueItem("LS001", "issuer-1", "dept-1", "receiver-1", "dept-2"),
|
issueItem("LS002", "issuer-2", "dept-3", "receiver-2", "dept-4")));
|
|
ArgumentCaptor<LambdaUpdateWrapper<DmsJiluDayinLiushuiEntity>> captor =
|
ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
|
verify(mapper, times(2)).update(isNull(), captor.capture());
|
Date operationTime = null;
|
for (LambdaUpdateWrapper<DmsJiluDayinLiushuiEntity> update : captor.getAllValues()) {
|
String sqlSet = update.getSqlSet();
|
assertTrue(sqlSet.contains("fafangren"));
|
assertTrue(sqlSet.contains("fafang_bumen"));
|
assertTrue(sqlSet.contains("jieshouren"));
|
assertTrue(sqlSet.contains("jieshou_bumen"));
|
assertTrue(sqlSet.contains("fafang_shijian"));
|
assertTrue(sqlSet.contains("jieshou_shijian"));
|
assertTrue(update.getSqlSegment().contains("f_delete_mark"));
|
assertTrue(update.getSqlSegment().contains("fafangren"));
|
for (Object value : update.getParamNameValuePairs().values()) {
|
if (value instanceof Date) {
|
if (operationTime == null) {
|
operationTime = (Date) value;
|
} else {
|
assertSame(operationTime, value);
|
}
|
}
|
}
|
}
|
assertNotNull(operationTime);
|
ArgumentCaptor<DmsJiluDayinShenqingZixiangEntity> updatedItem =
|
ArgumentCaptor.forClass(DmsJiluDayinShenqingZixiangEntity.class);
|
verify(itemMapper).update(updatedItem.capture(), any());
|
assertEquals("3", updatedItem.getValue().getYiqianfaShuliang());
|
assertEquals("2", updatedItem.getValue().getWeiqianfaShuliang());
|
verify(mapper).selectPrintItemForUpdate("item-1");
|
}
|
|
@Test
|
void rejectsIssueWhenPrintItemIsMissing() {
|
when(mapper.selectList(any())).thenReturn(java.util.List.of(
|
issueRecord("record-1", "LS001", null)));
|
when(mapper.update(isNull(), any())).thenReturn(1);
|
|
assertThrows(DataException.class,
|
() -> service.issue(issueForm(
|
issueItem("LS001", "issuer-1", "dept-1", "receiver-1", "dept-2"))));
|
verify(itemMapper, never()).update(any(DmsJiluDayinShenqingZixiangEntity.class), any());
|
}
|
|
@Test
|
void updatesIssueCountsForEachPrintItem() {
|
DmsJiluDayinLiushuiEntity first = issueRecord("record-1", "LS001", null);
|
DmsJiluDayinLiushuiEntity second = issueRecord("record-2", "LS002", null);
|
second.setDayinZixiangId("item-2");
|
DmsJiluDayinShenqingZixiangEntity firstItem = new DmsJiluDayinShenqingZixiangEntity();
|
firstItem.setDayinFenshu("3");
|
DmsJiluDayinShenqingZixiangEntity secondItem = new DmsJiluDayinShenqingZixiangEntity();
|
secondItem.setDayinFenshu("4");
|
secondItem.setYiqianfaShuliang("2");
|
when(mapper.selectList(any())).thenReturn(java.util.List.of(first, second));
|
when(mapper.update(isNull(), any())).thenReturn(1);
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(firstItem);
|
when(mapper.selectPrintItemForUpdate("item-2")).thenReturn(secondItem);
|
when(itemMapper.update(any(DmsJiluDayinShenqingZixiangEntity.class), any()))
|
.thenReturn(1);
|
|
service.issue(issueForm(issueItem("LS001", "issuer-1", "dept-1", "receiver-1", "dept-2"),
|
issueItem("LS002", "issuer-2", "dept-3", "receiver-2", "dept-4")));
|
|
ArgumentCaptor<DmsJiluDayinShenqingZixiangEntity> updatedItems =
|
ArgumentCaptor.forClass(DmsJiluDayinShenqingZixiangEntity.class);
|
verify(itemMapper, times(2)).update(updatedItems.capture(), any());
|
assertEquals("1", updatedItems.getAllValues().get(0).getYiqianfaShuliang());
|
assertEquals("2", updatedItems.getAllValues().get(0).getWeiqianfaShuliang());
|
assertEquals("3", updatedItems.getAllValues().get(1).getYiqianfaShuliang());
|
assertEquals("1", updatedItems.getAllValues().get(1).getWeiqianfaShuliang());
|
}
|
|
@Test
|
void accumulatesRecoveredCountsByPrintItem() {
|
DmsJiluDayinLiushuiEntity first = issueRecord("record-1", "LS001", null);
|
DmsJiluDayinLiushuiEntity second = issueRecord("record-2", "LS002", null);
|
DmsJiluDayinLiushuiEntity third = issueRecord("record-3", "LS003", null);
|
third.setDayinZixiangId("item-2");
|
DmsJiluDayinShenqingZixiangEntity firstItem = new DmsJiluDayinShenqingZixiangEntity();
|
firstItem.setYihuishouShuliang("1");
|
DmsJiluDayinShenqingZixiangEntity secondItem = new DmsJiluDayinShenqingZixiangEntity();
|
when(mapper.selectList(any())).thenReturn(java.util.List.of(first, second, third));
|
when(mapper.update(isNull(), any())).thenReturn(1);
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(firstItem);
|
when(mapper.selectPrintItemForUpdate("item-2")).thenReturn(secondItem);
|
when(itemMapper.update(any(DmsJiluDayinShenqingZixiangEntity.class), any()))
|
.thenReturn(1);
|
|
service.huishou(huishouForm("LS001", "LS002", "LS003"));
|
|
ArgumentCaptor<DmsJiluDayinShenqingZixiangEntity> updatedItems =
|
ArgumentCaptor.forClass(DmsJiluDayinShenqingZixiangEntity.class);
|
verify(itemMapper, times(2)).update(updatedItems.capture(), any());
|
assertEquals("3", updatedItems.getAllValues().get(0).getYihuishouShuliang());
|
assertEquals("1", updatedItems.getAllValues().get(1).getYihuishouShuliang());
|
verify(mapper, times(3)).update(isNull(), any());
|
}
|
|
@Test
|
void rejectsRecoveryWhenPrintItemIsMissing() {
|
when(mapper.selectList(any())).thenReturn(java.util.List.of(
|
issueRecord("record-1", "LS001", null)));
|
when(mapper.update(isNull(), any())).thenReturn(1);
|
|
assertThrows(DataException.class, () -> service.huishou(huishouForm("LS001")));
|
verify(itemMapper, never()).update(any(DmsJiluDayinShenqingZixiangEntity.class), any());
|
}
|
|
/**
|
* 验证任一流水已经发放时整批拒绝更新。
|
*/
|
@Test
|
void rejectsIssueBatchContainingIssuedRecord() {
|
when(mapper.selectList(any())).thenReturn(java.util.Arrays.asList(
|
issueRecord("record-1", "LS001", "issuer-0")));
|
|
DataException exception = assertThrows(DataException.class,
|
() -> service.issue(issueForm(
|
issueItem("LS001", "issuer-1", "dept-1", "receiver-1", "dept-2"))));
|
|
assertEquals("该条码已签收或发放请勿重复操作", exception.getMessage());
|
verify(mapper, never()).update(isNull(), any());
|
}
|
|
/**
|
* 验证发放方法声明成功和失败审计动作。
|
*/
|
@Test
|
void issueMethodDeclaresAuditLog() throws Exception {
|
AuditLog auditLog = DmsRecordPrintServiceImpl.class
|
.getMethod("issue", DmsRecordPrintIssueForm.class)
|
.getAnnotation(AuditLog.class);
|
|
assertNotNull(auditLog);
|
assertEquals("FAFANG", auditLog.action());
|
assertEquals("FAFANG_FAIL", auditLog.failureAction());
|
assertEquals("dms_jilu_dayin_liushui", auditLog.targetTable());
|
}
|
|
@Test
|
void confirmMethodDoesNotAddPrintAudit() throws Exception {
|
AuditLog auditLog = DmsRecordPrintServiceImpl.class
|
.getMethod("confirm", String.class)
|
.getAnnotation(AuditLog.class);
|
|
// 打印确认不额外记录正式审计,保留实际流水与业务状态。
|
assertNull(auditLog);
|
}
|
|
/**
|
* 验证成册预览按份数预占流水,但不更新 A、B 表。
|
*/
|
@Test
|
void preparesBoundCopiesWithoutPersistingPrintRecords() throws Exception {
|
DmsJiluDayinShenqingZixiangEntity item = item("chengce", "2", null);
|
prepareSuccessfulReservation(item, "JL001", "JL002");
|
|
DmsRecordPrintPrepareVO result = service.prepare("item-1");
|
|
assertEquals(2, result.getSerialCount());
|
assertEquals(2, result.getCopies().size());
|
assertEquals("JL001", result.getCopies().get(0).getSerialNo());
|
assertEquals("JL002", result.getCopies().get(1).getSerialNo());
|
assertNull(result.getCopies().get(0).getPageNo());
|
assertNull(result.getCopies().get(0).getRecordId());
|
verify(pageCountService, never()).getPageCount(any());
|
assertReservedMetadata(item, 2);
|
}
|
|
/**
|
* 验证分散打印的流水号数量等于去重页数乘以打印份数。
|
*/
|
@Test
|
void preparesLoosePageCopiesByPageCountTimesPrintCount() throws Exception {
|
DmsJiluDayinShenqingZixiangEntity item = item("fensan", "2", "3,1,3");
|
UUID fileCenterVersionId = UUID.fromString("00000000-0000-0000-0000-000000000002");
|
DmsWenjianBanbenEntity banben = new DmsWenjianBanbenEntity();
|
banben.setWenjianZhongxinWenjianId("00000000-0000-0000-0000-000000000001");
|
banben.setWenjianZhongxinBanbenId(fileCenterVersionId.toString());
|
DmsFilePageCountVO pageCount = new DmsFilePageCountVO();
|
pageCount.setPageCount(4);
|
when(banbenMapper.selectById("tenant-1", "business-version-1")).thenReturn(banben);
|
when(pageCountService.getPageCount(fileCenterVersionId)).thenReturn(pageCount);
|
prepareSuccessfulReservation(item, "JL001", "JL002", "JL003", "JL004");
|
|
DmsRecordPrintPrepareVO result = service.prepare("item-1");
|
|
assertEquals(4, result.getSerialCount());
|
assertEquals(java.util.Arrays.asList(1, 3), result.getSelectedPages());
|
assertEquals(1, result.getCopies().get(0).getCopyIndex());
|
assertEquals(1, result.getCopies().get(0).getPageNo());
|
assertEquals(3, result.getCopies().get(1).getPageNo());
|
assertEquals(2, result.getCopies().get(2).getCopyIndex());
|
verify(pageCountService).getPageCount(fileCenterVersionId);
|
assertReservedMetadata(item, 4);
|
}
|
|
/**
|
* 验证重复打开预览时复用原预占,不再次消耗流水号。
|
*/
|
@Test
|
void reusesExistingReservationWithoutTakingNewSerialNumbers() throws Exception {
|
DmsJiluDayinShenqingZixiangEntity item = item("chengce", "1", null);
|
DmsRecordPrintReservation reservation = reservation(item, "JL001");
|
UserInfo user = new UserInfo();
|
user.setUserId("user-1");
|
when(identity.requireUser()).thenReturn(user);
|
when(identity.tenantId(user)).thenReturn("tenant-1");
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(item);
|
when(mapper.countActivePrintRecords("item-1")).thenReturn(0);
|
when(reservationStore.findByPrintItem("tenant-1", "item-1"))
|
.thenReturn(reservation);
|
|
DmsRecordPrintPrepareVO result = service.prepare("item-1");
|
|
assertEquals("reservation-1", result.getReservationId());
|
assertEquals("JL001", result.getCopies().get(0).getSerialNo());
|
verify(mapper, never()).lockSerialAllocation();
|
verify(billRuleApi, never()).getBillNumber(any());
|
verify(reservationStore, never()).create(any(), anyLong());
|
}
|
|
/**
|
* 验证点击打印确认后才写入 A 表并更新 B 表。
|
*/
|
@Test
|
void confirmsReservedSerialNumbersAndPersistsPrintMetadata() throws Exception {
|
DmsJiluDayinShenqingZixiangEntity item = item("chengce", "2", null);
|
DmsRecordPrintReservation reservation = reservation(
|
item, "JL001", "JL002");
|
UserInfo user = new UserInfo();
|
user.setUserId("user-1");
|
when(identity.requireUser()).thenReturn(user);
|
when(identity.tenantId(user)).thenReturn("tenant-1");
|
when(reservationStore.findById("tenant-1", "reservation-1"))
|
.thenReturn(reservation);
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(item);
|
when(mapper.selectList(any())).thenReturn(java.util.Collections.emptyList());
|
when(mapper.countActivePrintRecords("item-1")).thenReturn(0);
|
when(mapper.insertPrintRecords(any())).thenReturn(2);
|
when(mapper.markPrintItemProcessing(any())).thenReturn(1);
|
|
DmsRecordPrintConfirmVO result = service.confirm("reservation-1");
|
|
assertEquals(Boolean.TRUE, result.getConfirmed());
|
assertEquals(2, result.getCopies().size());
|
assertEquals("JL001", result.getCopies().get(0).getSerialNo());
|
assertNotNull(result.getCopies().get(0).getRecordId());
|
assertPersistedMetadata(item, 2);
|
verify(reservationStore).markConfirmed(any(DmsRecordPrintReservation.class), anyLong());
|
}
|
|
/**
|
* 验证已确认预占的网络重试直接返回缓存结果,不重复写库。
|
*/
|
@Test
|
void returnsConfirmedReservationIdempotently() throws Exception {
|
DmsJiluDayinShenqingZixiangEntity item = item("chengce", "1", null);
|
DmsRecordPrintReservation reservation = reservation(item, "JL001");
|
reservation.setStatus(DmsRecordPrintReservation.STATUS_CONFIRMED);
|
reservation.setRecordIds(java.util.Collections.singletonList("record-1"));
|
UserInfo user = new UserInfo();
|
user.setUserId("user-1");
|
when(identity.requireUser()).thenReturn(user);
|
when(identity.tenantId(user)).thenReturn("tenant-1");
|
when(reservationStore.findById("tenant-1", "reservation-1"))
|
.thenReturn(reservation);
|
|
DmsRecordPrintConfirmVO result = service.confirm("reservation-1");
|
|
assertEquals("record-1", result.getCopies().get(0).getRecordId());
|
verify(mapper, never()).selectPrintItemForUpdate(any());
|
verify(mapper, never()).insertPrintRecords(any());
|
}
|
|
/**
|
* 验证已经存在 A 表记录时,在取号之前拒绝重复打印。
|
*/
|
@Test
|
void rejectsRepeatedPrintBeforeTakingSerialNumber() {
|
DmsJiluDayinShenqingZixiangEntity item = item("chengce", "1", null);
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(item);
|
when(mapper.countActivePrintRecords("item-1")).thenReturn(1);
|
|
DataException exception = assertThrows(DataException.class,
|
() -> service.prepare("item-1"));
|
|
assertEquals("该记录已经打印,不允许重复打印", exception.getMessage());
|
verify(mapper, never()).lockSerialAllocation();
|
verify(billRuleApi, never()).getBillNumber(any());
|
}
|
|
/**
|
* 配置一次成功打印需要的 Mapper 和流水号返回值。
|
*/
|
private void prepareSuccessfulReservation(
|
DmsJiluDayinShenqingZixiangEntity item, String... serialNumbers) throws Exception {
|
UserInfo user = new UserInfo();
|
user.setUserId("user-1");
|
when(identity.requireUser()).thenReturn(user);
|
when(identity.tenantId(user)).thenReturn("tenant-1");
|
when(mapper.selectPrintItemForUpdate("item-1")).thenReturn(item);
|
when(mapper.countActivePrintRecords("item-1")).thenReturn(0);
|
when(mapper.lockSerialAllocation()).thenReturn(1);
|
ActionResult<String>[] results = new ActionResult[serialNumbers.length];
|
for (int index = 0; index < serialNumbers.length; index++) {
|
results[index] = ActionResult.success("取号成功", serialNumbers[index]);
|
}
|
when(billRuleApi.getBillNumber("jiluDayinLiushuihao"))
|
.thenReturn(results[0], java.util.Arrays.copyOfRange(results, 1, results.length));
|
}
|
|
/**
|
* 断言预览只保存 Redis 预占,不写 A、B 表。
|
*/
|
private void assertReservedMetadata(
|
DmsJiluDayinShenqingZixiangEntity item, int expectedRecords) {
|
ArgumentCaptor<DmsRecordPrintReservation> captor =
|
ArgumentCaptor.forClass(DmsRecordPrintReservation.class);
|
verify(reservationStore).create(captor.capture(), anyLong());
|
DmsRecordPrintReservation reservation = captor.getValue();
|
assertEquals(expectedRecords, reservation.getSerialNumbers().size());
|
assertEquals("user-1", reservation.getUserId());
|
assertEquals("tenant-1", reservation.getTenantId());
|
assertEquals("item-1", reservation.getPrintItemId());
|
assertEquals(DmsRecordPrintReservation.STATUS_PENDING, reservation.getStatus());
|
verify(mapper, never()).insertPrintRecords(any());
|
verify(mapper, never()).markPrintItemProcessing(any());
|
assertNull(item.getDayinren());
|
assertNull(item.getDayinShijian());
|
assertEquals("daiqianfa", item.getDayinZhuangtai());
|
}
|
|
/**
|
* 断言确认阶段写入的所有 A 表记录与 B 表使用同一打印元数据。
|
*/
|
private void assertPersistedMetadata(
|
DmsJiluDayinShenqingZixiangEntity item, int expectedRecords) {
|
ArgumentCaptor<List<DmsJiluDayinLiushuiEntity>> captor =
|
ArgumentCaptor.forClass(List.class);
|
verify(mapper).insertPrintRecords(captor.capture());
|
List<DmsJiluDayinLiushuiEntity> records = captor.getValue();
|
assertEquals(expectedRecords, records.size());
|
Date printTime = records.get(0).getDayinShijian();
|
for (DmsJiluDayinLiushuiEntity record : records) {
|
assertEquals("user-1", record.getDayinren());
|
assertSame(printTime, record.getDayinShijian());
|
assertEquals("item-1", record.getDayinZixiangId());
|
assertEquals("print-1", record.getDayinId());
|
}
|
assertEquals("user-1", item.getDayinren());
|
assertSame(printTime, item.getDayinShijian());
|
assertEquals("dayinzhong", item.getDayinZhuangtai());
|
}
|
|
private DmsRecordPrintReservation reservation(
|
DmsJiluDayinShenqingZixiangEntity item, String... serialNumbers) {
|
DmsRecordPrintReservation reservation = new DmsRecordPrintReservation();
|
reservation.setReservationId("reservation-1");
|
reservation.setTenantId("tenant-1");
|
reservation.setUserId("user-1");
|
reservation.setPrintItemId(item.getId());
|
reservation.setItemVersion(item.getVersion());
|
reservation.setPrintType(item.getDayinLeixing());
|
reservation.setPrintCount(Integer.valueOf(item.getDayinFenshu()));
|
reservation.setSelectedPages(java.util.Collections.emptyList());
|
reservation.setSerialNumbers(java.util.Arrays.asList(serialNumbers));
|
reservation.setStatus(DmsRecordPrintReservation.STATUS_PENDING);
|
reservation.setCreatedAt(System.currentTimeMillis());
|
reservation.setExpiresAt(System.currentTimeMillis() + 60000L);
|
return reservation;
|
}
|
|
/**
|
* 构造可打印的 B 表实体。
|
*/
|
private DmsJiluDayinShenqingZixiangEntity item(
|
String printType, String printCount, String pages) {
|
DmsJiluDayinShenqingZixiangEntity item = new DmsJiluDayinShenqingZixiangEntity();
|
item.setId("item-1");
|
item.setForeignId("print-1");
|
item.setBanbenId("business-version-1");
|
item.setDayinLeixing(printType);
|
item.setDayinFenshu(printCount);
|
item.setYema(pages);
|
item.setYidayinFenshu("0");
|
item.setDayinZhuangtai("daiqianfa");
|
return item;
|
}
|
|
/** 构造已映射文件中心文件和版本的业务版本实体。 */
|
private DmsWenjianBanbenEntity businessVersion(String id) {
|
DmsWenjianBanbenEntity version = new DmsWenjianBanbenEntity();
|
version.setId(id);
|
version.setWenjianZhongxinWenjianId(UUID.randomUUID().toString());
|
version.setWenjianZhongxinBanbenId(UUID.randomUUID().toString());
|
return version;
|
}
|
|
/**
|
* 构造打印流水实体。
|
*/
|
private DmsJiluDayinLiushuiEntity issueRecord(
|
String id, String serialNumber, String issuer) {
|
DmsJiluDayinLiushuiEntity record = new DmsJiluDayinLiushuiEntity();
|
record.setId(id);
|
record.setLiushuihao(serialNumber);
|
record.setFafangren(issuer);
|
record.setDayinZixiangId("item-1");
|
return record;
|
}
|
|
/**
|
* 构造批量发放请求。
|
*/
|
private DmsRecordPrintIssueForm issueForm(DmsRecordPrintIssueItemForm... items) {
|
DmsRecordPrintIssueForm form = new DmsRecordPrintIssueForm();
|
form.setData(java.util.Arrays.asList(items));
|
return form;
|
}
|
|
private DmsRecordPrintHuishouForm huishouForm(String... serialNumbers) {
|
DmsRecordPrintHuishouForm form = new DmsRecordPrintHuishouForm();
|
java.util.List<DmsRecordPrintHuishouItemForm> items = new java.util.ArrayList<>();
|
for (String serialNumber : serialNumbers) {
|
DmsRecordPrintHuishouItemForm item = new DmsRecordPrintHuishouItemForm();
|
item.setLiushuihao(serialNumber);
|
item.setGuihuanren("returner");
|
item.setGuihuanBumen("return-dept");
|
item.setHuishouren("receiver");
|
item.setHuishouBumen("receive-dept");
|
items.add(item);
|
}
|
form.setData(items);
|
return form;
|
}
|
|
/**
|
* 构造单条发放明细。
|
*/
|
private DmsRecordPrintIssueItemForm issueItem(
|
String serialNumber, String issuer, String issuerDepartment,
|
String receiver, String receiverDepartment) {
|
DmsRecordPrintIssueItemForm item = new DmsRecordPrintIssueItemForm();
|
item.setLiushuihao(serialNumber);
|
item.setFafangren(issuer);
|
item.setFafangBumen(issuerDepartment);
|
item.setJieshouren(receiver);
|
item.setJieshouBumen(receiverDepartment);
|
return item;
|
}
|
}
|