package jnpf.limsService; import jnpf.base.service.SuperService; import jnpf.limsEntity.LimsModifySignPasswordParam; import jnpf.limsEntity.LimsSignEntity; import jnpf.limsEntity.LimsSignUsageTarget; import jnpf.limsEntity.LimsSignVerifyParam; import jnpf.limsEntity.SignOpType; import java.util.Collection; /** * LIMS 电子签名服务。 * 校验账户 + 签名密码后落一条 lims_sign,返回主键作为 biz_sign 凭证给前端。 */ public interface LimsSignService extends SuperService { /** * 校验签名并落 lims_sign 一条,返回新记录的主键 id(即给前端的 biz_sign)。 * *

校验链: *

    *
  1. opType 必须在 {@link jnpf.limsEntity.SignOpType} 枚举内
  2. *
  3. accountName 必须等于当前 session 用户的 account(防代签)
  4. *
  5. note 非空(前端可填 "N/A")
  6. *
  7. 查 lims_user_sign_password by user_id;无记录 → 抛 "未初始化签名密码"
  8. *
  9. password_hash = Md5(password + secretkey.toLowerCase()) 比对
  10. *
  11. 校验全通过后插入 lims_sign,返回 id
  12. *
* * @return lims_sign 主键 id * @throws jnpf.exception.DataException 校验失败 */ String verifyAndRecord(LimsSignVerifyParam param); /** * 校验指定用户签名并落 lims_sign 一条。 * *

与 {@link #verifyAndRecord(LimsSignVerifyParam)} 的差异: *

* * @return 校验通过后新建的签名记录 */ LimsSignEntity verifyUserAndRecord(LimsSignVerifyParam param); /** * 给所有未软删 base_user 批量初始化签名密码 = "123456"。 * 已有 lims_user_sign_password 记录的跳过。每个用户独立生成 secretkey。 * * @return [插入条数, 跳过条数] */ int[] initAllUsers(); /** * 修改 / 初始化当前登录用户的签名密码。 * *

校验链: *

    *
  1. code + timestamp 命中图形验证码缓存(不区分大小写)
  2. *
  3. 当前 session 用户存在
  4. *
  5. accountPassword 通过 Md5(accountPassword + secretkey) 比对 base_user.f_password
  6. *
  7. password / repeatPassword 均非空且相等
  8. *
  9. 生成新的 secretkey 与 hash,写入 / 更新 lims_user_sign_password,刷新 last_reset_time
  10. *
* *

用户首次设置签名密码(未存在 lims_user_sign_password 记录)和后续修改走同一接口, * 行为分别为 INSERT 与 UPDATE。 * * @throws jnpf.exception.DataException 任一校验失败 */ void modifySignPassword(LimsModifySignPasswordParam param); /** * 校验业务侧传入的 biz_sign(即 lims_sign 主键)真实有效。 * *

op_type 与 data_id 各自独立支持 lazy / eager(自方案 C 起): *

* 真正认领由 {@link #recordSignUsage} 在业务成功后原子完成,避免状态冲突时提前消费签名。 * 已有 {@code extra_json} 的签名视为已消费,必须重新签名,不能跨业务周期复用。 * *

expectedDataId 传 null = 业务接口不需要 data_id 锚点(如 jiance-task 批量类), * 此时完全跳过 data_id 校验,保留 verify 时的原值不动。 * *

签名人始终校验:必须等于当前 session 用户(防别人签的被偷用)。 * * @param signId 前端回传的 biz_sign(lims_sign 主键) * @param expectedOpType 业务期望的操作类型 * @param expectedDataId 业务期望的 data_id;传 null 表示不锚 data_id * @return 校验通过的签名记录 */ LimsSignEntity requireValidSign(String signId, SignOpType expectedOpType, String expectedDataId); /** * 在业务写库成功后记录签名被实际使用的事实。 * *

只接受业务 service 返回的实际成功目标;请求中因状态、权限或并发被跳过的目标不得传入。 * 实际目标为空时不认领签名、不更新 {@code lims_sign.extra_json},也不生成 {@code SIGN_USED}。 * 非空时先通过条件更新一次性原子认领签名,再将事件交给 AuditTxHolder 在事务提交后发布; * 已消费签名、认领冲突或结果保存失败会标记当前事务回滚。 */ void recordSignUsage(LimsSignEntity sign, SignOpType opType, Collection requestedTargetIds, Collection actualTargets); /** * 只做签名的原子认领,不发审计事件。 * *

给「签名事实由别处记录」的调用方用——当前是在线表单路径:表单保存本身已经产生层 0 * 事件,层 0 会据表单里的签名字段补写一条签名事件;这里再发一条 {@code SIGN_USED} * 就是同一次签名两条证据。 * *

认领逻辑必须保留:它是「签名单次消费」的防重放护栏(ed4d908), * 去掉等于同一个签名可以反复使用。 * *

与 {@link #recordSignUsage} 的关系是「同一段认领 + 是否再发事件」: * {@code recordSignUsage} 内部就调本方法,两处认领逻辑只有一份,不会各自漂移。 * 认领的全部前置条件(空目标不认领、认领冲突标记事务回滚)与 {@code recordSignUsage} 完全一致。 */ void claimSignUsageOnly(LimsSignEntity sign, SignOpType opType, Collection requestedTargetIds, Collection actualTargets); /** * 把任意结构数据序列化为 JSON 后回写到 lims_sign.extra_json。 * *

典型用法: *

* *

extra 为 null 时不更新。重复调用会覆盖(业务最新一次最权威)。 * * @param signId 签名记录主键(即 biz_sign) * @param extra 任意可序列化对象(Map / List / POJO 都可) */ void attachExtra(String signId, Object extra); }