电子签名

This commit is contained in:
2026-05-14 16:10:51 +08:00
parent 1c9cbfae59
commit 8f9d286bec
9 changed files with 671 additions and 0 deletions

View File

@@ -17,11 +17,14 @@ import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.bo.SysUserElectronicSignatureBo;
import org.dromara.system.domain.bo.SysUserPasswordBo;
import org.dromara.system.domain.bo.SysUserProfileBo;
import org.dromara.system.domain.vo.ProfileUserVo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserElectronicSignatureVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysUserElectronicSignatureService;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserService;
import org.springframework.http.MediaType;
@@ -45,6 +48,7 @@ public class SysProfileController extends BaseController {
private final ISysUserService userService;
private final ISysOssService ossService;
private final ISysUserLoginPortService sysUserLoginPortService;
private final ISysUserElectronicSignatureService userElectronicSignatureService;
/**
* 个人信息
@@ -93,6 +97,46 @@ public class SysProfileController extends BaseController {
return R.fail("修改个人信息异常,请联系管理员");
}
/**
* 当前登录人员电子签名信息
*/
@GetMapping("/electronic-signature/info")
public R<SysUserElectronicSignatureVo> electronicSignatureInfo() {
return R.ok(userElectronicSignatureService.getCurrentUserSignature());
}
/**
* 获取当前登录人员可直接用于业务回填的电子签名
*/
@GetMapping("/electronic-signature/current")
public R<SysUserElectronicSignatureVo> currentElectronicSignature() {
SysUserElectronicSignatureVo vo = userElectronicSignatureService.getCurrentUserSignature();
if (Boolean.FALSE.equals(vo.getConfigured())) {
return R.fail("暂未设置电子签名,请在个人资料内设置后重试或使用手动签名");
}
return R.ok(vo);
}
/**
* 保存当前登录人员电子签名
*/
@RepeatSubmit
@Log(title = "电子签名", businessType = BusinessType.UPDATE)
@PutMapping("/electronic-signature")
public R<SysUserElectronicSignatureVo> saveElectronicSignature(@Validated @RequestBody SysUserElectronicSignatureBo bo) {
return R.ok(userElectronicSignatureService.saveCurrentUserSignature(bo));
}
/**
* 清空当前登录人员电子签名
*/
@RepeatSubmit
@Log(title = "电子签名", businessType = BusinessType.UPDATE)
@DeleteMapping("/electronic-signature")
public R<Void> clearElectronicSignature() {
return toAjax(userElectronicSignatureService.clearCurrentUserSignature());
}
/**
* 重置密码
*

View File

@@ -0,0 +1,74 @@
package org.dromara.system.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 用户电子签名配置对象 sys_user_electronic_signature
*
* @author shihongwei
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user_electronic_signature")
public class SysUserElectronicSignature extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 人员唯一标识
*/
private String personKey;
/**
* 系统用户ID
*/
private Long userId;
/**
* 业务人员ID
*/
private String businessUserId;
/**
* 企业ID
*/
private Long companyId;
/**
* 登录端口
*/
private String loginPort;
/**
* 电子签名图片OSS ID
*/
private Long signOssId;
/**
* 签署人名称快照
*/
private String signName;
/**
* 状态1=已设置 0=未设置
*/
private String status;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,19 @@
package org.dromara.system.domain.bo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 当前登录人员电子签名保存参数
*
* @author shihongwei
*/
@Data
public class SysUserElectronicSignatureBo {
/**
* 电子签名图片OSS ID
*/
@NotNull(message = "电子签名图片不能为空")
private Long signOssId;
}

View File

@@ -0,0 +1,79 @@
package org.dromara.system.domain.vo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.system.domain.SysUserElectronicSignature;
import java.io.Serial;
import java.io.Serializable;
/**
* 当前登录人员电子签名信息
*
* @author shihongwei
*/
@Data
@AutoMapper(target = SysUserElectronicSignature.class)
public class SysUserElectronicSignatureVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
private Long id;
/**
* 是否已配置电子签名
*/
private Boolean configured;
/**
* 人员唯一标识
*/
private String personKey;
/**
* 系统用户ID
*/
private Long userId;
/**
* 业务人员ID
*/
private String businessUserId;
/**
* 企业ID
*/
private Long companyId;
/**
* 登录端口
*/
private String loginPort;
/**
* 电子签名图片OSS ID
*/
private Long signOssId;
/**
* 电子签名图片URL
*/
@Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "signOssId")
private String signUrl;
/**
* 签署人名称
*/
private String signName;
/**
* 状态1=已设置 0=未设置
*/
private String status;
}

View File

@@ -0,0 +1,15 @@
package org.dromara.system.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.system.domain.SysUserElectronicSignature;
import org.dromara.system.domain.vo.SysUserElectronicSignatureVo;
/**
* 用户电子签名配置Mapper接口
*
* @author shihongwei
*/
@Mapper
public interface SysUserElectronicSignatureMapper extends BaseMapperPlus<SysUserElectronicSignature, SysUserElectronicSignatureVo> {
}

View File

@@ -0,0 +1,34 @@
package org.dromara.system.service;
import org.dromara.system.domain.bo.SysUserElectronicSignatureBo;
import org.dromara.system.domain.vo.SysUserElectronicSignatureVo;
/**
* 当前登录人员电子签名服务
*
* @author shihongwei
*/
public interface ISysUserElectronicSignatureService {
/**
* 获取当前登录人员电子签名信息
*
* @return 电子签名信息
*/
SysUserElectronicSignatureVo getCurrentUserSignature();
/**
* 保存当前登录人员电子签名
*
* @param bo 保存参数
* @return 保存后的电子签名信息
*/
SysUserElectronicSignatureVo saveCurrentUserSignature(SysUserElectronicSignatureBo bo);
/**
* 清空当前登录人员电子签名
*
* @return 是否成功
*/
Boolean clearCurrentUserSignature();
}

View File

@@ -0,0 +1,166 @@
package org.dromara.system.service.impl;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.MimeTypeUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysUserElectronicSignature;
import org.dromara.system.domain.bo.SysUserElectronicSignatureBo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserElectronicSignatureVo;
import org.dromara.system.mapper.SysUserElectronicSignatureMapper;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserElectronicSignatureService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 当前登录人员电子签名服务实现
*
* @author shihongwei
*/
@RequiredArgsConstructor
@Service
public class SysUserElectronicSignatureServiceImpl implements ISysUserElectronicSignatureService {
private static final String STATUS_ENABLED = "1";
private static final String STATUS_DISABLED = "0";
private final SysUserElectronicSignatureMapper baseMapper;
private final ISysOssService ossService;
@Override
public SysUserElectronicSignatureVo getCurrentUserSignature() {
LoginUser loginUser = getRequiredLoginUser();
String personKey = buildPersonKey(loginUser);
SysUserElectronicSignature entity = baseMapper.selectOne(
Wrappers.<SysUserElectronicSignature>lambdaQuery()
.eq(SysUserElectronicSignature::getTenantId, loginUser.getTenantId())
.eq(SysUserElectronicSignature::getPersonKey, personKey)
.last("limit 1")
);
if (entity == null) {
return buildEmptyVo(loginUser, personKey);
}
SysUserElectronicSignatureVo vo = MapstructUtils.convert(entity, SysUserElectronicSignatureVo.class);
vo.setConfigured(isConfigured(entity));
if (StringUtils.isBlank(vo.getSignName())) {
vo.setSignName(resolveSignName(loginUser));
}
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public SysUserElectronicSignatureVo saveCurrentUserSignature(SysUserElectronicSignatureBo bo) {
LoginUser loginUser = getRequiredLoginUser();
validateSignatureOss(bo.getSignOssId());
String personKey = buildPersonKey(loginUser);
SysUserElectronicSignature entity = baseMapper.selectOne(
Wrappers.<SysUserElectronicSignature>lambdaQuery()
.eq(SysUserElectronicSignature::getTenantId, loginUser.getTenantId())
.eq(SysUserElectronicSignature::getPersonKey, personKey)
.last("limit 1")
);
if (entity == null) {
entity = new SysUserElectronicSignature();
entity.setTenantId(loginUser.getTenantId());
entity.setPersonKey(personKey);
}
entity.setUserId(loginUser.getUserId());
entity.setBusinessUserId(loginUser.getBusinessUserId());
entity.setCompanyId(loginUser.getCompanyId());
entity.setLoginPort(defaultLoginPort(loginUser));
entity.setSignOssId(bo.getSignOssId());
entity.setSignName(resolveSignName(loginUser));
entity.setStatus(STATUS_ENABLED);
if (entity.getId() == null) {
baseMapper.insert(entity);
} else {
baseMapper.updateById(entity);
}
return getCurrentUserSignature();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean clearCurrentUserSignature() {
LoginUser loginUser = getRequiredLoginUser();
String personKey = buildPersonKey(loginUser);
SysUserElectronicSignature entity = baseMapper.selectOne(
Wrappers.<SysUserElectronicSignature>lambdaQuery()
.eq(SysUserElectronicSignature::getTenantId, loginUser.getTenantId())
.eq(SysUserElectronicSignature::getPersonKey, personKey)
.last("limit 1")
);
if (entity == null) {
return true;
}
entity.setUserId(loginUser.getUserId());
entity.setBusinessUserId(loginUser.getBusinessUserId());
entity.setCompanyId(loginUser.getCompanyId());
entity.setLoginPort(defaultLoginPort(loginUser));
entity.setSignOssId(null);
entity.setSignName(resolveSignName(loginUser));
entity.setStatus(STATUS_DISABLED);
return baseMapper.updateById(entity) > 0;
}
private boolean isConfigured(SysUserElectronicSignature entity) {
return entity != null
&& STATUS_ENABLED.equals(entity.getStatus())
&& entity.getSignOssId() != null;
}
private SysUserElectronicSignatureVo buildEmptyVo(LoginUser loginUser, String personKey) {
SysUserElectronicSignatureVo vo = new SysUserElectronicSignatureVo();
vo.setConfigured(false);
vo.setPersonKey(personKey);
vo.setUserId(loginUser.getUserId());
vo.setBusinessUserId(loginUser.getBusinessUserId());
vo.setCompanyId(loginUser.getCompanyId());
vo.setLoginPort(defaultLoginPort(loginUser));
vo.setSignName(resolveSignName(loginUser));
vo.setStatus(STATUS_DISABLED);
return vo;
}
private void validateSignatureOss(Long signOssId) {
SysOssVo ossVo = ossService.getById(signOssId);
if (ossVo == null) {
throw new ServiceException("电子签名文件不存在,请重新上传后再试");
}
String fileName = StringUtils.defaultIfBlank(ossVo.getOriginalName(), ossVo.getFileName());
String extension = FileUtil.extName(fileName);
if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
throw new ServiceException("电子签名必须为图片格式");
}
}
private LoginUser getRequiredLoginUser() {
LoginUser loginUser = LoginHelper.getLoginUser();
if (loginUser == null || loginUser.getUserId() == null) {
throw new ServiceException("用户未登录");
}
return loginUser;
}
private String buildPersonKey(LoginUser loginUser) {
String businessUserId = StringUtils.defaultIfBlank(loginUser.getBusinessUserId(), String.valueOf(loginUser.getUserId()));
String companyId = loginUser.getCompanyId() == null ? "0" : String.valueOf(loginUser.getCompanyId());
return defaultLoginPort(loginUser) + ":" + companyId + ":" + businessUserId;
}
private String defaultLoginPort(LoginUser loginUser) {
return StringUtils.defaultIfBlank(loginUser.getLoginPort(), "default");
}
private String resolveSignName(LoginUser loginUser) {
return StringUtils.defaultIfBlank(loginUser.getNickname(), loginUser.getUsername());
}
}