package jnpf.limsController;
|
|
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
import jnpf.exception.DataException;
|
import jnpf.limsEntity.BizStatus;
|
import jnpf.limsEntity.LimsQingyandanEntity;
|
import jnpf.limsEntity.LimsSignEntity;
|
import jnpf.limsEntity.LimsSignUsageTarget;
|
import jnpf.limsEntity.SignOpType;
|
import jnpf.limsService.LimsQingyandanService;
|
import jnpf.limsService.LimsSignService;
|
import jnpf.util.R;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RestController;
|
|
import java.util.Collections;
|
import java.util.Map;
|
|
@Slf4j
|
@Tag(name = "lims请验单接口", description = "请验单提交 / 撤回 / 废弃 / 编辑")
|
@RestController
|
@RequestMapping("/lims/biz")
|
public class LimsQingyandanController {
|
|
@Autowired
|
private LimsQingyandanService limsQingyandanService;
|
@Autowired
|
private LimsSignService limsSignService;
|
|
/**
|
* 成品请验单提交 -> 复制数据到取样表
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
@Operation(summary = "成品请验单提交")
|
@PostMapping("/qingyandan/submit")
|
public R<String> qingyandanSubmit(@RequestBody LimsQingyandanEntity formData) {
|
LimsQingyandanEntity qingyandan = limsQingyandanService.findByFormData(formData);
|
if (qingyandan == null) {
|
return R.error("请验单数据不存在");
|
}
|
|
BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
|
if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
|
&& currentStatus != BizStatus.REJECTED) {
|
return R.error("当前状态[" + currentStatus.getLabel() + "]不允许提交");
|
}
|
|
LimsSignEntity sign;
|
try {
|
sign = requireQingyandanSign(qingyandan, formData, SignOpType.QINGYANDAN_SUBMIT);
|
} catch (DataException e) {
|
return R.error(e.getMessage());
|
}
|
limsQingyandanService.submit(qingyandan);
|
claimQingyandanSignUsageOnly(sign, SignOpType.QINGYANDAN_SUBMIT, qingyandan);
|
return R.success("提交成功");
|
}
|
|
/**
|
* 请验单操作:撤回 / 废弃
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
@Operation(summary = "请验单操作(撤回/废弃)")
|
@PostMapping("/qingyandan/action")
|
public R<String> qingyandanAction(@RequestBody Map<String, String> params) {
|
if (params == null) {
|
return R.error("请求参数不能为空");
|
}
|
String action = params.get("action");
|
String liushuihao = params.get("jianyan_liushuihao");
|
|
if (liushuihao == null || liushuihao.isEmpty()) {
|
return R.error("检验流水号不能为空");
|
}
|
|
LimsQingyandanEntity qingyandan = limsQingyandanService.findByLiushuihao(liushuihao);
|
if (qingyandan == null) {
|
return R.error("请验单数据不存在");
|
}
|
|
BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
|
|
switch (action) {
|
case "cancel":
|
if (currentStatus != BizStatus.CONFIRMED) {
|
return R.error("只有已提交状态的请验单才能撤回");
|
}
|
LimsSignEntity cancelSign;
|
try {
|
cancelSign = requireQingyandanActionSign(
|
qingyandan, params, SignOpType.QINGYANDAN_CANCEL);
|
} catch (DataException e) {
|
return R.error(e.getMessage());
|
}
|
if (!limsQingyandanService.cancel(qingyandan)) {
|
return R.error("请验单状态已变更,撤回未生效");
|
}
|
recordQingyandanSignUsage(
|
cancelSign, SignOpType.QINGYANDAN_CANCEL, qingyandan);
|
return R.success("撤回成功");
|
|
case "delete":
|
if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
|
&& currentStatus != BizStatus.REJECTED) {
|
return R.error("只有已创建或已撤回状态的请验单才能废弃");
|
}
|
limsQingyandanService.delete(qingyandan);
|
return R.success("废弃成功");
|
|
default:
|
return R.error("不支持的操作: " + action);
|
}
|
}
|
|
/**
|
* 请验单编辑
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
@Operation(summary = "请验单编辑")
|
@PostMapping("/qingyandan/edit")
|
public R<String> qingyandanEdit(@RequestBody LimsQingyandanEntity formData) {
|
LimsQingyandanEntity qingyandan = limsQingyandanService.findByFormData(formData);
|
if (qingyandan == null) {
|
return R.error("请验单数据不存在");
|
}
|
|
BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
|
if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
|
&& currentStatus != BizStatus.REJECTED) {
|
return R.error("当前状态[" + currentStatus.getLabel() + "]不允许编辑");
|
}
|
|
LimsSignEntity sign;
|
try {
|
sign = requireQingyandanSign(qingyandan, formData, SignOpType.QINGYANDAN_SUBMIT);
|
} catch (DataException e) {
|
return R.error(e.getMessage());
|
}
|
limsQingyandanService.edit(qingyandan, formData);
|
claimQingyandanSignUsageOnly(sign, SignOpType.QINGYANDAN_SUBMIT, qingyandan);
|
return R.success("编辑成功");
|
}
|
|
private LimsSignEntity requireQingyandanSign(LimsQingyandanEntity persisted,
|
LimsQingyandanEntity request,
|
SignOpType opType) {
|
String signId = request == null ? null : request.getBizSign();
|
if (signId == null || signId.trim().isEmpty()) {
|
throw new DataException("请添加电子签名");
|
}
|
signId = signId.trim();
|
persisted.setBizSign(signId);
|
if (request != null) {
|
request.setBizSign(signId);
|
}
|
return limsSignService.requireValidSign(
|
signId, opType, persisted.getJianyanLiushuihao());
|
}
|
|
private LimsSignEntity requireQingyandanActionSign(LimsQingyandanEntity persisted,
|
Map<String, String> params,
|
SignOpType opType) {
|
String signId = params.get("biz_sign");
|
if (signId == null || signId.trim().isEmpty()) {
|
signId = params.get("id");
|
}
|
if (signId == null || signId.trim().isEmpty()) {
|
throw new DataException("请添加电子签名");
|
}
|
return limsSignService.requireValidSign(
|
signId.trim(), opType, persisted.getJianyanLiushuihao());
|
}
|
|
/**
|
* 认领签名并记一条 {@code SIGN_USED}。
|
*
|
* <p>🔴 <b>只剩撤回({@code QINGYANDAN_CANCEL})走这条路,一字未动</b>:撤回是列表按钮场景,
|
* 层 0 根本不覆盖(实测撤回只产生层 2 + 层 3 两条事件,没有层 0 伴随事件)。
|
* 一旦让它也改走只认领,那次签名就彻底没有任何记录。
|
*
|
* <p>提交 / 编辑改走 {@link #claimQingyandanSignUsageOnly}——见那里的说明。
|
*/
|
private void recordQingyandanSignUsage(LimsSignEntity sign,
|
SignOpType opType,
|
LimsQingyandanEntity qingyandan) {
|
limsSignService.recordSignUsage(sign, opType,
|
Collections.singletonList(qingyandan.getId()),
|
Collections.singletonList(qingyandanUsageTarget(qingyandan)));
|
}
|
|
/**
|
* 只做签名的原子认领,<b>不发 {@code SIGN_USED} 事件</b>(2026-07-31,D8)。
|
*
|
* <p>提交 / 编辑走的是在线表单路径:表单保存本身已经产生层 0 事件,层 0 会据表单里的
|
* {@code biz_sign} 字段补写一条签名事件;这里再发一条 {@code SIGN_USED} 就是同一次签名两条证据。
|
*
|
* <p><b>认领没有被删</b>:它是「签名单次消费」的防重放护栏(ed4d908),
|
* 去掉等于同一个签名可以反复使用。本次改动只是把「认领」和「发事件」拆开。
|
*/
|
private void claimQingyandanSignUsageOnly(LimsSignEntity sign,
|
SignOpType opType,
|
LimsQingyandanEntity qingyandan) {
|
limsSignService.claimSignUsageOnly(sign, opType,
|
Collections.singletonList(qingyandan.getId()),
|
Collections.singletonList(qingyandanUsageTarget(qingyandan)));
|
}
|
|
private LimsSignUsageTarget qingyandanUsageTarget(LimsQingyandanEntity qingyandan) {
|
return LimsSignUsageTarget.builder()
|
.targetTable("lims_qingyandan")
|
.targetId(qingyandan.getId())
|
.bizType("QINGYANDAN")
|
.bizModule("LIMS-请验单")
|
.bizCode(qingyandan.getJianyanLiushuihao())
|
.build();
|
}
|
}
|