package jnpf.dmsService;
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.print.DmsPrintOperationEntity;
|
import jnpf.dmsEntity.print.DmsPrintOperationLogEntity;
|
import jnpf.dmsEntity.print.form.DmsPrintOperationCreateForm;
|
import jnpf.dmsEntity.print.form.DmsPrintOperationQuery;
|
import jnpf.dmsEntity.print.form.DmsPrintTargetForm;
|
import jnpf.dmsEntity.print.vo.DmsPrintOperationCreatedVO;
|
import jnpf.dmsEntity.print.vo.DmsPrintOperationLogVO;
|
import jnpf.dmsMapper.DmsPrintOperationLogMapper;
|
import jnpf.dmsMapper.DmsPrintOperationMapper;
|
import jnpf.dmsService.support.DmsPrintOperationRequestContext;
|
import jnpf.exception.DataException;
|
import jnpf.util.RandomUtil;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.UUID;
|
import java.util.stream.Collectors;
|
|
@Service
|
@RequiredArgsConstructor
|
public class DmsPrintOperationLogService {
|
private static final int MAX_TARGETS = 200;
|
private static final List<String> TARGET_TYPES = java.util.Arrays.asList("DOCUMENT", "BUSINESS_RECORD", "BLANK_FORM");
|
private static final List<String> PRINT_TYPES = java.util.Arrays.asList(
|
"CONTROLLED_DOCUMENT", "RECORD_DOCUMENT", "BLANK_FORM", "LABEL", "ARCHIVE_BOX", "OTHER");
|
private static final List<String> SOURCE_TYPES = java.util.Arrays.asList("FILE_PREVIEW", "FORM_DETAIL", "LIST_BATCH");
|
|
private final DmsPrintOperationMapper operationMapper;
|
private final DmsPrintOperationLogMapper logMapper;
|
private final DmsPrintOperationRequestContext requestContext;
|
|
@Transactional(rollbackFor = Exception.class)
|
public DmsPrintOperationCreatedVO create(DmsPrintOperationCreateForm form) throws DataException {
|
UserInfo user = requestContext.currentUser();
|
if (user == null || !StringUtils.hasText(user.getUserId())) {
|
throw new DataException("缺少登录用户信息");
|
}
|
String clientEventId = uuidText(form == null ? null : form.getClientEventId(), "clientEventId");
|
String printType = enumText(form.getPrintType(), PRINT_TYPES, "printType");
|
String sourceType = enumText(form.getSourceType(), SOURCE_TYPES, "sourceType");
|
int copies = form.getCopies() == null ? 1 : form.getCopies();
|
if (copies < 1 || copies > 999) {
|
throw new DataException("copies 必须在 1 到 999 之间");
|
}
|
String remark = optional(form.getRemark(), 500, "remark");
|
String printName = required(form.getPrintName(), 300, "printName");
|
List<NormalizedTarget> targets = normalizeTargets(form.getTargets(), printName);
|
|
String requestHash = requestHash(printType, sourceType, copies, remark, targets);
|
Date now = new Date();
|
DmsPrintOperationEntity operation = new DmsPrintOperationEntity();
|
operation.setId(RandomUtil.uuId());
|
operation.setClientEventId(clientEventId);
|
operation.setRequestHash(requestHash);
|
operation.setTargetCount(targets.size());
|
operation.setCreatedAt(now);
|
|
if (operationMapper.insertIgnore(operation) == 0) {
|
DmsPrintOperationEntity existing = operationMapper.selectByClientEventId(clientEventId);
|
if (existing == null || !requestHash.equals(existing.getRequestHash())) {
|
throw new DataException("CLIENT_EVENT_CONFLICT:相同 clientEventId 对应了不同打印请求");
|
}
|
return new DmsPrintOperationCreatedVO(existing.getId(), existing.getTargetCount());
|
}
|
|
String menuId = optional(requestContext.header("Jnpf-Menu-Id"), 100, "Jnpf-Menu-Id");
|
String menuName = menuId == null ? null : logMapper.selectMenuNameById(menuId);
|
String parentId = optional(requestContext.header("Jnpf-Parent-Id"), 100, "Jnpf-Parent-Id");
|
String parentBizModule = menuId == null || parentId == null ? null
|
: logMapper.selectParentBizModuleName(menuId, parentId);
|
String operatorName = optional(user.getUserName(), 200, "operatorName");
|
if (operatorName == null) {
|
operatorName = user.getUserId().trim();
|
}
|
String requestIp = optional(requestContext.requestIp(), 64, "requestIp");
|
String userAgent = optional(requestContext.userAgent(), 1000, "userAgent");
|
for (NormalizedTarget target : targets) {
|
DmsPrintOperationLogEntity row = new DmsPrintOperationLogEntity();
|
row.setId(RandomUtil.uuId());
|
row.setOperationId(operation.getId());
|
row.setTargetType(target.targetType);
|
row.setTargetId(target.targetId);
|
row.setDocumentId(target.documentId);
|
row.setDocumentCode(target.documentCode);
|
row.setDocumentName(target.documentName);
|
row.setBatchNo(target.batchNo);
|
row.setVersionNo(target.versionNo);
|
row.setPrintType(printType);
|
row.setCopies(copies);
|
row.setStatus("INITIATED");
|
row.setSourceType(sourceType);
|
row.setSourceMenuId(menuId);
|
row.setSourceMenuName(menuName);
|
row.setParentBizModule(parentBizModule);
|
row.setOperatorId(user.getUserId().trim());
|
row.setOperatorName(operatorName);
|
row.setOperatedAt(now);
|
row.setRemark(target.remark == null ? remark : target.remark);
|
row.setRequestIp(requestIp);
|
row.setUserAgent(userAgent);
|
logMapper.insert(row);
|
}
|
return new DmsPrintOperationCreatedVO(operation.getId(), targets.size());
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPrintOperationLogVO> list(DmsPrintOperationQuery query) throws DataException {
|
DmsPrintOperationQuery q = query == null ? new DmsPrintOperationQuery() : query;
|
if (q.getPageSize() > 200) {
|
throw new DataException("pageSize 不能超过 200");
|
}
|
Page<DmsPrintOperationLogEntity> page = new Page<>(q.getCurrentPage(), q.getPageSize());
|
Page<DmsPrintOperationLogEntity> result = logMapper.selectPage(page, queryWrapper(q));
|
List<DmsPrintOperationLogVO> values = result.getRecords().stream().map(this::toVO).collect(Collectors.toList());
|
q.setData(values, result.getTotal());
|
return values;
|
}
|
|
@Transactional(readOnly = true)
|
public DmsPrintOperationLogVO detail(String id) throws DataException {
|
DmsPrintOperationLogEntity row = logMapper.selectById(id);
|
if (row == null) {
|
throw new DataException("打印记录不存在");
|
}
|
return toVO(row);
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPrintOperationLogVO> export(DmsPrintOperationQuery query) {
|
DmsPrintOperationQuery q = query == null ? new DmsPrintOperationQuery() : query;
|
return logMapper.selectList(queryWrapper(q).last("LIMIT 100000")).stream().map(this::toVO).collect(Collectors.toList());
|
}
|
|
private LambdaQueryWrapper<DmsPrintOperationLogEntity> queryWrapper(DmsPrintOperationQuery q) {
|
LambdaQueryWrapper<DmsPrintOperationLogEntity> wrapper = new LambdaQueryWrapper<>();
|
String keyword = trim(q.getKeyword());
|
if (keyword != null) {
|
wrapper.and(w -> w.like(DmsPrintOperationLogEntity::getDocumentCode, keyword)
|
.or().like(DmsPrintOperationLogEntity::getDocumentName, keyword)
|
.or().like(DmsPrintOperationLogEntity::getBatchNo, keyword)
|
.or().like(DmsPrintOperationLogEntity::getVersionNo, keyword)
|
.or().like(DmsPrintOperationLogEntity::getOperatorName, keyword)
|
.or().like(DmsPrintOperationLogEntity::getRemark, keyword));
|
}
|
like(wrapper, DmsPrintOperationLogEntity::getDocumentCode, q.getDocumentCode());
|
like(wrapper, DmsPrintOperationLogEntity::getDocumentName, q.getDocumentName());
|
like(wrapper, DmsPrintOperationLogEntity::getBatchNo, q.getBatchNo());
|
like(wrapper, DmsPrintOperationLogEntity::getVersionNo, q.getVersionNo());
|
like(wrapper, DmsPrintOperationLogEntity::getOperatorName, q.getOperatorName());
|
if (StringUtils.hasText(q.getPrintType())) wrapper.eq(DmsPrintOperationLogEntity::getPrintType, q.getPrintType().trim());
|
if (StringUtils.hasText(q.getOperatorId())) wrapper.eq(DmsPrintOperationLogEntity::getOperatorId, q.getOperatorId().trim());
|
if (q.getStartTime() != null) wrapper.ge(DmsPrintOperationLogEntity::getOperatedAt, q.getStartTime());
|
if (q.getEndTime() != null) wrapper.lt(DmsPrintOperationLogEntity::getOperatedAt, q.getEndTime());
|
return wrapper.orderByDesc(DmsPrintOperationLogEntity::getOperatedAt)
|
.orderByDesc(DmsPrintOperationLogEntity::getId);
|
}
|
|
private void like(LambdaQueryWrapper<DmsPrintOperationLogEntity> wrapper,
|
com.baomidou.mybatisplus.core.toolkit.support.SFunction<DmsPrintOperationLogEntity, ?> column,
|
String value) {
|
if (StringUtils.hasText(value)) wrapper.like(column, value.trim());
|
}
|
|
private List<NormalizedTarget> normalizeTargets(List<DmsPrintTargetForm> raw, String printName) throws DataException {
|
if (raw == null || raw.isEmpty() || raw.size() > MAX_TARGETS) {
|
throw new DataException("targets 数量必须在 1 到 " + MAX_TARGETS + " 之间");
|
}
|
Map<String, NormalizedTarget> unique = new LinkedHashMap<>();
|
for (DmsPrintTargetForm value : raw) {
|
if (value == null) throw new DataException("targets 不能包含空项");
|
NormalizedTarget target = new NormalizedTarget();
|
target.targetType = enumText(value.getTargetType(), TARGET_TYPES, "targetType");
|
target.targetId = required(value.getTargetId(), 200, "targetId");
|
target.documentId = value.getDocumentId();
|
target.documentCode = optional(value.getDocumentCode(), 300, "documentCode");
|
target.documentName = optional(value.getDocumentName(), 300, "documentName");
|
target.batchNo = optional(value.getBatchNo(), 200, "batchNo");
|
target.versionNo = optional(value.getVersionNo(), 100, "versionNo");
|
target.remark = optional(value.getRemark(), 500, "target.remark");
|
if (target.documentCode == null) target.documentCode = target.targetId;
|
if (target.documentName == null) target.documentName = printName;
|
unique.putIfAbsent(target.targetType + ':' + target.targetId, target);
|
}
|
return new ArrayList<>(unique.values());
|
}
|
|
private String requestHash(String printType, String sourceType, int copies, String remark,
|
List<NormalizedTarget> targets) {
|
StringBuilder value = new StringBuilder();
|
append(value, printType); append(value, sourceType); append(value, String.valueOf(copies)); append(value, remark);
|
targets.stream().sorted((a, b) -> (a.targetType + a.targetId).compareTo(b.targetType + b.targetId)).forEach(t -> {
|
append(value, t.targetType); append(value, t.targetId); append(value, t.documentCode);
|
append(value, t.documentName); append(value, t.batchNo); append(value, t.versionNo); append(value, t.remark);
|
});
|
return DigestUtil.sha256Hex(value.toString());
|
}
|
|
private void append(StringBuilder value, String item) {
|
String text = item == null ? "" : item;
|
value.append(text.length()).append(':').append(text).append('|');
|
}
|
|
private DmsPrintOperationLogVO toVO(DmsPrintOperationLogEntity row) {
|
DmsPrintOperationLogVO vo = new DmsPrintOperationLogVO();
|
org.springframework.beans.BeanUtils.copyProperties(row, vo);
|
return vo;
|
}
|
|
private String enumText(String value, List<String> allowed, String name) throws DataException {
|
String normalized = required(value, 50, name).toUpperCase();
|
if (!allowed.contains(normalized)) throw new DataException(name + " 取值无效");
|
return normalized;
|
}
|
|
private String uuidText(String value, String name) throws DataException {
|
String normalized = required(value, 50, name);
|
try { return UUID.fromString(normalized).toString(); }
|
catch (IllegalArgumentException ex) { throw new DataException(name + " 必须是 UUID"); }
|
}
|
|
private String required(String value, int max, String name) throws DataException {
|
String normalized = optional(value, max, name);
|
if (normalized == null) throw new DataException(name + " 不能为空");
|
return normalized;
|
}
|
|
private String optional(String value, int max, String name) throws DataException {
|
String normalized = trim(value);
|
if (normalized != null && normalized.length() > max) throw new DataException(name + " 长度不能超过 " + max);
|
return normalized;
|
}
|
|
private String trim(String value) { return value == null || value.trim().isEmpty() ? null : value.trim(); }
|
|
private static class NormalizedTarget {
|
private String targetType;
|
private String targetId;
|
private String documentId;
|
private String documentCode;
|
private String documentName;
|
private String batchNo;
|
private String versionNo;
|
private String remark;
|
}
|
}
|