package jnpf.dmsService.impl;
|
|
import jnpf.base.Pagination;
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.DmsWenjianBianhaoEntity;
|
import jnpf.dmsEntity.DmsWenjianBianhaoGuizeEntity;
|
import jnpf.dmsEntity.DmsWenjianBianhaoGuizeZixiangEntity;
|
import jnpf.dmsEntity.DmsWenjianBianhaoShenqingEntity;
|
import jnpf.dmsEntity.DmsWenjianMingchengBiangengShenqingEntity;
|
import jnpf.dmsEntity.wenjianbianhao.DmsFlowEventType;
|
import jnpf.dmsEntity.wenjianbianhao.vo.DmsWenjianBianhaoOptionVO;
|
import jnpf.dmsEntity.wenjianbianhao.vo.DmsWenjianBianhaoPreviewVO;
|
import jnpf.dmsEntity.wenjianbianhao.vo.DmsWenjianBianhaoRuleVO;
|
import jnpf.dmsMapper.DmsWenjianBianhaoGuizeMapper;
|
import jnpf.dmsMapper.DmsWenjianBianhaoGuizeZixiangMapper;
|
import jnpf.dmsMapper.DmsWenjianBianhaoLiushuiJishuMapper;
|
import jnpf.dmsMapper.DmsWenjianBianhaoMapper;
|
import jnpf.dmsMapper.DmsWenjianBianhaoShenqingMapper;
|
import jnpf.dmsMapper.DmsWenjianMingchengBiangengShenqingMapper;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsService.DmsWenjianBianhaoApplicationService;
|
import jnpf.dmsService.DmsWenjianBianhaoPermissionService;
|
import jnpf.dmsService.support.DmsWenjianBianhaoFlowStateResolver;
|
import jnpf.dmsService.support.DmsWenjianBianhaoFlowStateResolver.Action;
|
import jnpf.exception.DataException;
|
import jnpf.permission.UserApi;
|
import jnpf.permission.entity.UserEntity;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class DmsWenjianBianhaoApplicationServiceImpl implements DmsWenjianBianhaoApplicationService {
|
|
private static final String DISABLED = "disabled";
|
private static final String DRAFT = "draft";
|
private static final String PENDING_REVIEW = "pending_review";
|
private static final String USING = "using";
|
private static final String REJECTED = "rejected";
|
private static final String CANCELLED = "cancelled";
|
private static final Pattern SERIAL_TOKEN = Pattern.compile("\\{SERIAL:(\\d+)\\}");
|
|
private final DmsCurrentIdentity identity;
|
private final DmsWenjianBianhaoPermissionService permissionService;
|
private final DmsWenjianBianhaoGuizeMapper ruleMapper;
|
private final DmsWenjianBianhaoGuizeZixiangMapper partMapper;
|
private final DmsWenjianBianhaoShenqingMapper applicationMapper;
|
private final DmsWenjianBianhaoMapper numberMapper;
|
private final DmsWenjianBianhaoLiushuiJishuMapper counterMapper;
|
private final DmsWenjianMingchengBiangengShenqingMapper nameChangeMapper;
|
private final UserApi userApi;
|
|
@Override
|
public List<DmsWenjianBianhaoRuleVO> enabledRules(
|
Pagination pagination, String guizeId, String searchValue) throws DataException {
|
UserInfo user = identity.requireUser();
|
String tenantId = identity.tenantId(user);
|
List<DmsWenjianBianhaoRuleVO> result = new ArrayList<>();
|
List<DmsWenjianBianhaoGuizeEntity> rules = ruleMapper.selectEnabled(tenantId);
|
if (rules == null || rules.isEmpty()) {
|
pagination.setTotal(0);
|
return Collections.emptyList();
|
}
|
String resolvedRuleId = trim(guizeId);
|
String keyword = blank(searchValue) ? trim(pagination.getKeyword()) : trim(searchValue);
|
for (DmsWenjianBianhaoGuizeEntity rule : rules) {
|
if (!resolvedRuleId.isEmpty() && !resolvedRuleId.equals(rule.getId())) {
|
continue;
|
}
|
if (!keyword.isEmpty() && !matchesRule(rule, keyword)) {
|
continue;
|
}
|
if (!permissionService.canApplyRule(user.getUserId(), rule.getId())) {
|
continue;
|
}
|
DmsWenjianBianhaoRuleVO vo = new DmsWenjianBianhaoRuleVO();
|
vo.setId(rule.getId());
|
vo.setGuizeMingcheng(rule.getGuizeMingcheng());
|
vo.setGuizeLeixing(rule.getGuizeLeixing());
|
vo.setWenjianMulu(rule.getWenjianMulu());
|
RuleSnapshot snapshot = loadSnapshot(tenantId, rule.getId());
|
vo.setYulanBianhao(snapshot.preview);
|
vo.setGuizeXianshi(display(rule.getGuizeMingcheng(), snapshot.preview));
|
result.add(vo);
|
}
|
pagination.setTotal(result.size());
|
long pageSize = Math.max(pagination.getPageSize(), 1L);
|
long currentPage = Math.max(pagination.getCurrentPage(), 1L);
|
if (currentPage - 1 > Long.MAX_VALUE / pageSize) {
|
return Collections.emptyList();
|
}
|
long offset = (currentPage - 1) * pageSize;
|
if (offset >= result.size()) {
|
return Collections.emptyList();
|
}
|
int fromIndex = (int) offset;
|
long available = result.size() - offset;
|
int toIndex = (int) (offset + Math.min(pageSize, available));
|
return new ArrayList<>(result.subList(fromIndex, toIndex));
|
}
|
|
private boolean matchesRule(DmsWenjianBianhaoGuizeEntity rule, String keyword) {
|
String normalized = keyword.toLowerCase();
|
return trim(rule.getGuizeMingcheng()).toLowerCase().contains(normalized)
|
|| trim(rule.getGuizeLeixing()).toLowerCase().contains(normalized)
|
|| trim(rule.getWenjianMulu()).toLowerCase().contains(normalized);
|
}
|
|
@Override
|
public DmsWenjianBianhaoPreviewVO preview(String guizeId) throws DataException {
|
UserInfo user = identity.requireUser();
|
requirePermission(user.getUserId(), guizeId);
|
RuleSnapshot snapshot = loadSnapshot(identity.tenantId(user), guizeId);
|
return new DmsWenjianBianhaoPreviewVO(guizeId, snapshot.preview, snapshot.hasSerial);
|
}
|
|
@Override
|
public List<DmsWenjianBianhaoOptionVO> usingNumbers(
|
Pagination pagination, String bianhaoId, String searchValue) throws DataException {
|
UserInfo user = identity.requireUser();
|
String tenantId = identity.tenantId(user);
|
if (blank(pagination.getKeyword()) && !blank(searchValue)) {
|
pagination.setKeyword(trim(searchValue));
|
}
|
List<DmsWenjianBianhaoEntity> numbers = numberMapper.selectUsing(
|
tenantId, trim(bianhaoId), pagination);
|
if (numbers == null || numbers.isEmpty()) {
|
return Collections.emptyList();
|
}
|
List<DmsWenjianBianhaoEntity> allowedNumbers = new ArrayList<>(numbers.size());
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
if (permissionService.canViewNumber(user.getUserId(), number.getId())) {
|
allowedNumbers.add(number);
|
}
|
}
|
Map<String, String> userNames = loadUserNames(allowedNumbers);
|
List<DmsWenjianBianhaoOptionVO> result = new ArrayList<>(allowedNumbers.size());
|
for (DmsWenjianBianhaoEntity number : allowedNumbers) {
|
DmsWenjianBianhaoOptionVO vo = new DmsWenjianBianhaoOptionVO();
|
vo.setId(number.getId());
|
vo.setWenjianBianhao(number.getWenjianBianhao());
|
vo.setWenjianMingcheng(number.getDangqianWenjianMingcheng());
|
vo.setMingchengBanben(number.getMingchengBanben());
|
vo.setGuizeMingcheng(number.getGuizeMingchengKuaizhao());
|
vo.setShiyongRenId(number.getShiyongRenId());
|
vo.setShiyongRen(userNames.get(number.getShiyongRenId()));
|
vo.setBianhaoFafangShijian(number.getBianhaoFafangShijian());
|
vo.setBianhaoXianshi(display(number.getWenjianBianhao(),
|
number.getDangqianWenjianMingcheng()));
|
result.add(vo);
|
}
|
return result;
|
}
|
|
private Map<String, String> loadUserNames(List<DmsWenjianBianhaoEntity> numbers) {
|
List<String> userIds = new ArrayList<>();
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
String userId = trim(number.getShiyongRenId());
|
if (!userId.isEmpty() && !userIds.contains(userId)) {
|
userIds.add(userId);
|
}
|
}
|
if (userIds.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
List<UserEntity> users = userApi.getUserName(userIds);
|
if (users == null || users.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
Map<String, String> result = new HashMap<>();
|
for (UserEntity user : users) {
|
if (user != null && !blank(user.getId())) {
|
result.put(user.getId(), user.getRealName());
|
}
|
}
|
return result;
|
}
|
|
private String display(String first, String second) {
|
String left = trim(first);
|
String right = trim(second);
|
if (left.isEmpty()) {
|
return right;
|
}
|
return right.isEmpty() ? left : left + " / " + right;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void syncApplicationFlow(String flowId, String taskId, Integer flowState,
|
DmsFlowEventType eventType) throws DataException {
|
UserInfo currentUser = identity.requireUser();
|
String tenantId = identity.tenantId(currentUser);
|
String resolvedFlowId = requireText(flowId, "流程ID不能为空");
|
String resolvedTaskId = requireText(taskId, "流程任务ID不能为空");
|
Action action = resolveAction(flowState);
|
|
DmsWenjianBianhaoShenqingEntity application = findApplicationForUpdate(
|
tenantId, resolvedFlowId, resolvedTaskId);
|
verifyPersistedFlowState(application.getFlowState(), flowState, eventType);
|
List<DmsWenjianBianhaoEntity> numbers = numberMapper.selectByApplicationForUpdate(
|
tenantId, application.getId());
|
if (numbers == null || numbers.isEmpty()) {
|
throw new DataException("文件编号申请至少需要一条编号明细");
|
}
|
|
switch (action) {
|
case RESTORE_DRAFT:
|
changeUnissuedStatus(numbers, DRAFT);
|
break;
|
case FREEZE_SNAPSHOT:
|
freezeSnapshots(application, numbers);
|
break;
|
case ISSUE_NUMBER:
|
issueNumbers(tenantId, numbers);
|
break;
|
case REJECT:
|
changeUnissuedStatus(numbers, REJECTED);
|
break;
|
case CANCEL:
|
changeUnissuedStatus(numbers, CANCELLED);
|
break;
|
case REVOKE:
|
revokeUnissuedNumbers(numbers);
|
break;
|
case HOLD:
|
break;
|
case ERROR:
|
throw new DataException("文件编号申请流程处于异常状态,请修复流程后重试");
|
default:
|
throw new DataException("不支持的文件编号申请流程动作:" + action);
|
}
|
log.info("文件编号申请流程事件同步完成,flowId={}, taskId={}, event={}, flowState={}, action={}",
|
resolvedFlowId, resolvedTaskId, eventType, flowState, action);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void syncNameChangeFlow(String flowId, String taskId, Integer flowState,
|
DmsFlowEventType eventType) throws DataException {
|
UserInfo currentUser = identity.requireUser();
|
String tenantId = identity.tenantId(currentUser);
|
String resolvedFlowId = requireText(flowId, "流程ID不能为空");
|
String resolvedTaskId = requireText(taskId, "流程任务ID不能为空");
|
Action action = resolveAction(flowState);
|
|
DmsWenjianMingchengBiangengShenqingEntity change = findNameChangeForUpdate(
|
tenantId, resolvedFlowId, resolvedTaskId);
|
verifyPersistedFlowState(change.getFlowState(), flowState, eventType);
|
|
if (action == Action.FREEZE_SNAPSHOT) {
|
freezeNameChange(tenantId, change);
|
} else if (action == Action.ISSUE_NUMBER) {
|
completeNameChange(tenantId, change);
|
} else if (action == Action.ERROR) {
|
throw new DataException("文件名称变更流程处于异常状态,请修复流程后重试");
|
}
|
log.info("文件名称变更流程事件同步完成,flowId={}, taskId={}, event={}, flowState={}, action={}",
|
resolvedFlowId, resolvedTaskId, eventType, flowState, action);
|
}
|
|
private DmsWenjianBianhaoShenqingEntity findApplicationForUpdate(
|
String tenantId, String flowId, String taskId) throws DataException {
|
List<DmsWenjianBianhaoShenqingEntity> applications =
|
applicationMapper.selectByFlowForUpdate(tenantId, flowId, taskId);
|
if (applications == null || applications.isEmpty()) {
|
throw new DataException("未找到文件编号申请流程关联记录");
|
}
|
if (applications.size() > 1) {
|
throw new DataException("多个文件编号申请关联同一流程任务");
|
}
|
return applications.get(0);
|
}
|
|
private DmsWenjianMingchengBiangengShenqingEntity findNameChangeForUpdate(
|
String tenantId, String flowId, String taskId) throws DataException {
|
List<DmsWenjianMingchengBiangengShenqingEntity> changes =
|
nameChangeMapper.selectByFlowForUpdate(tenantId, flowId, taskId);
|
if (changes == null || changes.isEmpty()) {
|
throw new DataException("未找到文件名称变更流程关联记录");
|
}
|
if (changes.size() > 1) {
|
throw new DataException("多个文件名称变更申请关联同一流程任务");
|
}
|
return changes.get(0);
|
}
|
|
private void verifyPersistedFlowState(Integer persistedState, Integer workflowState,
|
DmsFlowEventType eventType) throws DataException {
|
if (eventType == null) {
|
throw new DataException("流程事件类型不能为空");
|
}
|
if (!Objects.equals(persistedState, workflowState)) {
|
throw new DataException("流程状态尚未同步完成:表单状态=" + persistedState
|
+ ",任务状态=" + workflowState + ",请稍后重试");
|
}
|
}
|
|
private Action resolveAction(Integer flowState) throws DataException {
|
return DmsWenjianBianhaoFlowStateResolver.resolve(flowState)
|
.orElseThrow(() -> new DataException("未知的工作流状态:" + flowState));
|
}
|
|
private void freezeSnapshots(DmsWenjianBianhaoShenqingEntity application,
|
List<DmsWenjianBianhaoEntity> numbers) throws DataException {
|
String applicantId = requireText(application.getShenqingRenId(), "编号申请人不能为空");
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
if (!blank(number.getWenjianBianhao())) {
|
throw new DataException("已发放的文件编号不能重新提交:" + number.getWenjianBianhao());
|
}
|
String originalName = requireText(number.getYuanWenjianMingcheng(), "文件名称不能为空");
|
if (hasReusableSnapshot(number)) {
|
refreshUnissuedBasicData(number, applicantId, originalName);
|
continue;
|
}
|
|
requirePermission(applicantId, number.getGuizeId());
|
RuleSnapshot snapshot = loadSnapshot(application.getTenantId(), number.getGuizeId());
|
|
number.setGuizeKuaizhaoId(number.getGuizeId());
|
number.setGuizeMingchengKuaizhao(snapshot.ruleName);
|
number.setGuizeKuaizhao(snapshot.json);
|
number.setBianhaoMuban(snapshot.template);
|
number.setYulanBianhao(snapshot.preview);
|
number.setShenqingBianhao(snapshot.preview);
|
if (blank(number.getShiyongRenId())) {
|
number.setShiyongRenId(applicantId);
|
}
|
number.setDangqianWenjianMingcheng(originalName);
|
number.setMingchengBanben(1);
|
number.setBizStatus(PENDING_REVIEW);
|
requireSingleUpdate(numberMapper.updateById(number), "冻结文件编号规则快照失败");
|
}
|
}
|
|
private boolean hasReusableSnapshot(DmsWenjianBianhaoEntity number) {
|
return PENDING_REVIEW.equals(number.getBizStatus())
|
&& !blank(number.getGuizeKuaizhao())
|
&& !blank(number.getBianhaoMuban())
|
&& Objects.equals(number.getGuizeKuaizhaoId(), number.getGuizeId());
|
}
|
|
private void refreshUnissuedBasicData(DmsWenjianBianhaoEntity number, String applicantId,
|
String originalName) throws DataException {
|
boolean changed = false;
|
if (blank(number.getShiyongRenId())) {
|
number.setShiyongRenId(applicantId);
|
changed = true;
|
}
|
if (!Objects.equals(number.getDangqianWenjianMingcheng(), originalName)
|
|| !Integer.valueOf(1).equals(number.getMingchengBanben())) {
|
number.setDangqianWenjianMingcheng(originalName);
|
number.setMingchengBanben(1);
|
changed = true;
|
}
|
if (!Objects.equals(number.getShenqingBianhao(), number.getYulanBianhao())) {
|
number.setShenqingBianhao(number.getYulanBianhao());
|
changed = true;
|
}
|
if (changed) {
|
requireSingleUpdate(numberMapper.updateById(number), "刷新文件编号申请基础数据失败");
|
}
|
}
|
|
private void issueNumbers(String tenantId,
|
List<DmsWenjianBianhaoEntity> numbers) throws DataException {
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
if (!blank(number.getWenjianBianhao())) {
|
boolean changed = false;
|
if (!Objects.equals(number.getShenqingBianhao(), number.getWenjianBianhao())) {
|
number.setShenqingBianhao(number.getWenjianBianhao());
|
changed = true;
|
}
|
if (!USING.equals(number.getBizStatus())) {
|
number.setBizStatus(USING);
|
changed = true;
|
}
|
if (changed) {
|
requireSingleUpdate(numberMapper.updateById(number), "恢复已发放文件编号状态失败");
|
}
|
continue;
|
}
|
if (!PENDING_REVIEW.equals(number.getBizStatus())
|
|| blank(number.getGuizeKuaizhao())
|
|| blank(number.getBianhaoMuban())
|
|| blank(number.getGuizeKuaizhaoId())) {
|
throw new DataException("文件编号缺少发起时冻结的规则快照,不能发号:" + number.getId());
|
}
|
|
AssignedNumber assigned = renderFinalNumber(tenantId, number);
|
number.setWenjianBianhao(assigned.number);
|
number.setShenqingBianhao(assigned.number);
|
number.setLiushuiHao(assigned.serial);
|
number.setBianhaoFafangShijian(new Date());
|
number.setBizStatus(USING);
|
requireSingleUpdate(numberMapper.updateById(number), "写入正式文件编号失败");
|
}
|
}
|
|
private AssignedNumber renderFinalNumber(String tenantId,
|
DmsWenjianBianhaoEntity number) throws DataException {
|
String template = number.getBianhaoMuban();
|
Matcher matcher = SERIAL_TOKEN.matcher(template);
|
if (!matcher.find()) {
|
return new AssignedNumber(template, null);
|
}
|
int start = matcher.start();
|
int end = matcher.end();
|
int width = parseSerialWidth(matcher.group(1));
|
if (matcher.find()) {
|
throw new DataException("一个编号规则只能配置一个流水号组成项");
|
}
|
Long value = counterMapper.next(tenantId, number.getGuizeKuaizhaoId(), template);
|
if (value == null) {
|
throw new DataException("流水号分配失败");
|
}
|
String serial = leftPad(value, width);
|
return new AssignedNumber(template.substring(0, start) + serial + template.substring(end), serial);
|
}
|
|
private void changeUnissuedStatus(List<DmsWenjianBianhaoEntity> numbers,
|
String targetStatus) throws DataException {
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
if (!blank(number.getWenjianBianhao()) || USING.equals(number.getBizStatus())) {
|
continue;
|
}
|
if (targetStatus.equals(number.getBizStatus())) {
|
continue;
|
}
|
number.setBizStatus(targetStatus);
|
requireSingleUpdate(numberMapper.updateById(number), "更新文件编号状态失败");
|
}
|
}
|
|
private void revokeUnissuedNumbers(List<DmsWenjianBianhaoEntity> numbers) throws DataException {
|
for (DmsWenjianBianhaoEntity number : numbers) {
|
if (!blank(number.getWenjianBianhao()) || USING.equals(number.getBizStatus())) {
|
log.warn("已发放编号关联流程被撤销,保留编号并等待人工处理,numberId={}, number={}",
|
number.getId(), number.getWenjianBianhao());
|
continue;
|
}
|
if (!CANCELLED.equals(number.getBizStatus())) {
|
number.setBizStatus(CANCELLED);
|
requireSingleUpdate(numberMapper.updateById(number), "撤销文件编号失败");
|
}
|
}
|
}
|
|
private void freezeNameChange(String tenantId,
|
DmsWenjianMingchengBiangengShenqingEntity change) throws DataException {
|
String numberId = requireText(change.getBianhaoId(), "文件编号ID不能为空");
|
requireText(change.getBiangengHouWenjianMingcheng(), "变更后文件名称不能为空");
|
DmsWenjianBianhaoEntity number = numberMapper.selectActiveByIdForUpdate(tenantId, numberId);
|
if (number == null || !USING.equals(number.getBizStatus()) || blank(number.getWenjianBianhao())) {
|
throw new DataException("文件编号不存在或尚未发放");
|
}
|
if (nameChangeMapper.countActiveByNumber(tenantId, numberId, change.getId()) > 0) {
|
throw new DataException("该文件编号已有进行中的名称变更申请,请勿重复提交");
|
}
|
change.setYuanWenjianMingcheng(number.getDangqianWenjianMingcheng());
|
change.setYuanMingchengBanben(currentNameVersion(number));
|
requireSingleUpdate(nameChangeMapper.updateById(change), "冻结文件名称版本失败");
|
}
|
|
private void completeNameChange(String tenantId,
|
DmsWenjianMingchengBiangengShenqingEntity change) throws DataException {
|
String numberId = requireText(change.getBianhaoId(), "文件编号ID不能为空");
|
String newName = requireText(change.getBiangengHouWenjianMingcheng(), "变更后文件名称不能为空");
|
if (change.getYuanMingchengBanben() == null) {
|
throw new DataException("名称变更申请缺少发起时冻结的名称版本");
|
}
|
|
DmsWenjianBianhaoEntity number = numberMapper.selectActiveByIdForUpdate(tenantId, numberId);
|
if (number == null || !USING.equals(number.getBizStatus())) {
|
throw new DataException("文件编号不存在或尚未发放");
|
}
|
int currentVersion = currentNameVersion(number);
|
int expectedVersion = change.getYuanMingchengBanben();
|
if (currentVersion == expectedVersion + 1
|
&& Objects.equals(number.getDangqianWenjianMingcheng(), newName)) {
|
return;
|
}
|
if (currentVersion != expectedVersion) {
|
throw new DataException("文件名称已被其他变更流程更新,请重新发起");
|
}
|
number.setDangqianWenjianMingcheng(newName);
|
number.setMingchengBanben(currentVersion + 1);
|
requireSingleUpdate(numberMapper.updateById(number), "更新文件名称失败");
|
}
|
|
private int currentNameVersion(DmsWenjianBianhaoEntity number) {
|
return number.getMingchengBanben() == null ? 1 : number.getMingchengBanben();
|
}
|
|
private RuleSnapshot loadSnapshot(String tenantId, String ruleId) throws DataException {
|
String resolvedRuleId = requireText(ruleId, "编号规则ID不能为空");
|
String resolvedTenantId = requireText(tenantId, "租户ID不能为空");
|
DmsWenjianBianhaoGuizeEntity rule = ruleMapper.selectActiveById(
|
resolvedTenantId, resolvedRuleId);
|
if (rule == null || DISABLED.equals(rule.getBizEnabled())) {
|
throw new DataException("文件编号规则不存在或已停用");
|
}
|
List<DmsWenjianBianhaoGuizeZixiangEntity> parts = partMapper.selectActiveByRule(
|
resolvedTenantId, resolvedRuleId);
|
if (parts == null || parts.isEmpty()) {
|
throw new DataException("文件编号规则未配置明细");
|
}
|
|
StringBuilder template = new StringBuilder();
|
StringBuilder json = new StringBuilder();
|
json.append("{\"ruleId\":\"").append(escape(resolvedRuleId))
|
.append("\",\"ruleName\":\"").append(escape(rule.getGuizeMingcheng()))
|
.append("\",\"ruleType\":\"").append(escape(rule.getGuizeLeixing()))
|
.append("\",\"parts\":[");
|
boolean hasSerial = false;
|
for (int i = 0; i < parts.size(); i++) {
|
DmsWenjianBianhaoGuizeZixiangEntity part = parts.get(i);
|
String type = trim(part.getBianhaoZuheLeixing());
|
String value = trim(part.getZhi());
|
String separator = trim(part.getFengefu());
|
if (isSerialType(type)) {
|
if (hasSerial) {
|
throw new DataException("一个编号规则只能配置一个流水号组成项");
|
}
|
int width = serialTemplateWidth(value);
|
template.append("{SERIAL:").append(width).append('}');
|
hasSerial = true;
|
} else {
|
template.append(value);
|
}
|
template.append(separator);
|
if (i > 0) {
|
json.append(',');
|
}
|
json.append("{\"order\":").append(i + 1)
|
.append(",\"id\":\"").append(escape(part.getId()))
|
.append("\",\"type\":\"").append(escape(type))
|
.append("\",\"value\":\"").append(escape(value))
|
.append("\",\"separator\":\"").append(escape(separator)).append("\"}");
|
}
|
json.append("]}");
|
String preview = SERIAL_TOKEN.matcher(template).replaceAll("待审");
|
return new RuleSnapshot(rule.getGuizeMingcheng(), template.toString(), preview,
|
hasSerial, json.toString());
|
}
|
|
private void requirePermission(String userId, String ruleId) throws DataException {
|
String resolvedRuleId = requireText(ruleId, "编号规则ID不能为空");
|
if (!permissionService.canApplyRule(userId, resolvedRuleId)) {
|
throw new DataException("无权申请该文件编号规则");
|
}
|
}
|
|
private int serialTemplateWidth(String value) throws DataException {
|
if (blank(value) || !value.matches("0+")) {
|
throw new DataException("流水号模板必须由一个或多个0组成");
|
}
|
return value.length();
|
}
|
|
private boolean isSerialType(String type) {
|
return "liushuihao".equalsIgnoreCase(type) || "流水号".equals(type);
|
}
|
|
private int parseSerialWidth(String width) throws DataException {
|
try {
|
int value = Integer.parseInt(width);
|
if (value < 1) {
|
throw new NumberFormatException();
|
}
|
return value;
|
} catch (NumberFormatException e) {
|
throw new DataException("流水号模板长度无效:" + width);
|
}
|
}
|
|
private String leftPad(Long value, int width) throws DataException {
|
String text = String.valueOf(value);
|
if (text.length() > width) {
|
throw new DataException("流水号超过规则配置长度");
|
}
|
return String.format("%0" + width + "d", value);
|
}
|
|
private void requireSingleUpdate(int affectedRows, String message) throws DataException {
|
if (affectedRows != 1) {
|
throw new DataException(message + ",请刷新后重试");
|
}
|
}
|
|
private String requireText(String value, String message) throws DataException {
|
String resolved = trim(value);
|
if (resolved.isEmpty()) {
|
throw new DataException(message);
|
}
|
return resolved;
|
}
|
|
private boolean blank(String value) {
|
return trim(value).isEmpty();
|
}
|
|
private String trim(String value) {
|
return value == null ? "" : value.trim();
|
}
|
|
private String escape(String value) {
|
return trim(value).replace("\\", "\\\\").replace("\"", "\\\"");
|
}
|
|
private static final class RuleSnapshot {
|
private final String ruleName;
|
private final String template;
|
private final String preview;
|
private final boolean hasSerial;
|
private final String json;
|
|
private RuleSnapshot(String ruleName, String template, String preview,
|
boolean hasSerial, String json) {
|
this.ruleName = ruleName;
|
this.template = template;
|
this.preview = preview;
|
this.hasSerial = hasSerial;
|
this.json = json;
|
}
|
}
|
|
private static final class AssignedNumber {
|
private final String number;
|
private final String serial;
|
|
private AssignedNumber(String number, String serial) {
|
this.number = number;
|
this.serial = serial;
|
}
|
}
|
}
|