刘光辉
5 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
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
package jnpf.dmsService;
 
import jnpf.base.ActionResult;
import jnpf.base.BillRuleApi;
import jnpf.dmsEntity.fenfa.*;
import jnpf.dmsMapper.DmsWenjianDayinJishuMapper;
import jnpf.exception.DataException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
 
@Service
@RequiredArgsConstructor
public class DmsWenjianDayinHaomaService {
    private final DmsWenjianDayinJishuMapper mapper;
    private final BillRuleApi billRuleApi;
 
    /** 调用方持有任务行锁;新任务创建同事务取号,旧任务仅补齐尚未打印的份号。 */
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
    public List<DmsWenjianDayinHaoma> ensureTaskNumbers(String tenant, DmsWenjianDayinRenwuEntity task, int consumed) {
        if (task == null || task.getId() == null || task.getDayinFenshu() == null
                || task.getDayinFenshu() < 1 || consumed < 0 || consumed > task.getDayinFenshu())
            throw new DataException("打印任务份数或已消耗序号无效");
        List<DmsWenjianDayinHaoma> saved = mapper.selectTaskNumbers(tenant, task.getId());
        if (!saved.isEmpty()) {
            validateSaved(task, consumed, saved);
            return saved;
        }
        int remaining = task.getDayinFenshu() - consumed;
        if (remaining <= 0) return saved;
        List<DmsWenjianDayinHaoma> allocated = reserve(tenant, task, remaining);
        for (int i = 0; i < allocated.size(); i++) {
            DmsWenjianDayinHaoma number = allocated.get(i);
            number.setId(UUID.randomUUID().toString()); number.setTaskSequence(consumed + i + 1);
            if (mapper.insertTaskNumber(tenant, task.getId(), number) != 1) throw new DataException("保存任务固定份号失败");
        }
        return allocated;
    }
 
    /** 回传仅验证既有固定号码,绝不在确认阶段补号。 */
    public void requireReservedNumbers(String tenant, DmsWenjianDayinRenwuEntity task,
                                      int consumed, List<DmsWenjianDayinHaoma> frozen) {
        List<DmsWenjianDayinHaoma> saved = mapper.selectTaskNumbers(tenant, task.getId());
        validateSaved(task, consumed, saved);
        List<DmsWenjianDayinHaoma> expected = saved.stream()
                .filter(number -> number.getTaskSequence() > consumed).limit(frozen.size()).toList();
        if (!expected.equals(frozen)) throw new DataException("回传号码与任务固定份号不一致");
    }
 
    private void validateSaved(DmsWenjianDayinRenwuEntity task, int consumed,
                               List<DmsWenjianDayinHaoma> saved) {
        int total = task.getDayinFenshu();
        if (saved.isEmpty() || saved.size() > total) throw new DataException("任务固定份号缺失");
        int first = total - saved.size() + 1;
        if (first > consumed + 1) throw new DataException("任务固定份号不完整");
        Set<String> unique = new HashSet<>();
        for (int i = 0; i < saved.size(); i++) {
            DmsWenjianDayinHaoma number = saved.get(i);
            if (number == null || !Objects.equals(number.getTaskSequence(), first + i)
                    || number.getId() == null || number.getCounterId() == null
                    || number.getYear() == null || number.getSequence() == null
                    || number.getSequence() < 1 || number.getSequence() > 9999
                    || number.getSerialNumber() == null || number.getSerialNumber().isBlank()
                    || !unique.add(number.getSerialNumber())
                    || !String.format(Locale.ROOT, "%04d%04d", number.getYear(), number.getSequence()).equals(number.getCountSerialNumber()))
                throw new DataException("任务固定份号不完整或已损坏,不能重新取号");
        }
    }
 
    /** 必须与任务创建/首次旧任务补号处于同一事务。已提交的号码永不回收。 */
    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
    public List<DmsWenjianDayinHaoma> reserve(String tenant, DmsWenjianDayinRenwuEntity task, int copies) {
        if (copies < 1 || copies > 9999 || task.getWenjianBianhao() == null || task.getWenjianBianhao().isBlank()
                || task.getBanbenhao() == null || task.getBanbenhao().isBlank()) throw new DataException("打印计数组或份数无效");
        int year = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).getYear();
        DmsWenjianDayinJishu counter = mapper.reserve(UUID.randomUUID().toString(), tenant,
                task.getWenjianBianhao(), task.getBanbenhao(), year, copies);
        if (counter == null) throw new DataException("该文件版本本年度计数流水已超过9999,不能继续取号");
        List<DmsWenjianDayinHaoma> numbers = new ArrayList<>();
        for (int i = counter.getValue() - copies + 1; i <= counter.getValue(); i++) {
            ActionResult<String> result = billRuleApi.getBillNumber("wenjianDayinLiushuihao");
            if (result == null || result.getData() == null || result.getData().isBlank()) throw new DataException("打印唯一流水取号失败");
            DmsWenjianDayinHaoma number = new DmsWenjianDayinHaoma();
            number.setCounterId(counter.getId()); number.setYear(year); number.setSequence(i);
            number.setSerialNumber(result.getData());
            number.setCountSerialNumber(String.format(Locale.ROOT, "%04d%04d", year, i));
            numbers.add(number);
        }
        return numbers;
    }
}