6 changed files with 306 additions and 0 deletions
@ -0,0 +1,167 @@ |
|||||
|
package com.zdxt.enforcementcode.controller.web.supervision; |
||||
|
|
||||
|
import cn.hutool.core.util.ObjectUtil; |
||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
|
import com.zdxt.enforcementcode.common.util.CommonUtil; |
||||
|
import com.zdxt.enforcementcode.domain.bo.ChangePhonenumberBo; |
||||
|
import com.zdxt.enforcementcode.common.util.ZwSmsClient; |
||||
|
import com.zdxt.enforcementcode.domain.bo.QueryUserInfoBo; |
||||
|
import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo; |
||||
|
import com.zdxt.enforcementcode.service.thirdparty.ISIQService; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import jakarta.validation.constraints.Pattern; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.dromara.common.core.domain.R; |
||||
|
import org.dromara.common.core.utils.StringUtils; |
||||
|
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.SysUser; |
||||
|
import org.dromara.system.mapper.SysUserMapper; |
||||
|
import org.dromara.system.service.ICryptoSensitiveHsmService; |
||||
|
import org.dromara.system.service.ISysUserService; |
||||
|
import org.dromara.system.domain.vo.SysUserVo; |
||||
|
import org.springframework.validation.annotation.Validated; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
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.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 监督端-通用接口 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Validated |
||||
|
@RequiredArgsConstructor |
||||
|
@RestController |
||||
|
@RequestMapping("/web/sysuser/common") |
||||
|
public class SysUserCommonController extends BaseController { |
||||
|
|
||||
|
private final ISysUserService userService; |
||||
|
private final SysUserMapper sysUserMapper; |
||||
|
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService; |
||||
|
|
||||
|
/** |
||||
|
* 更改用户手机号码 |
||||
|
* <p>密评双轨口径: phonenumber 列写 HSM 密文(SM4-CBC 随机 IV, 不可等值比较), |
||||
|
* crypto_remark_two 备份 AES 原逻辑值; 判重/查询均走备份轨道</p> |
||||
|
* |
||||
|
* @param bo 更改手机号参数(userId + phonenumber) |
||||
|
*/ |
||||
|
@PostMapping("/changePhonenumber") |
||||
|
public R<Void> changePhonenumber(@Validated @RequestBody ChangePhonenumberBo bo) { |
||||
|
Long userId = bo.getUserId(); |
||||
|
String phonenumber = bo.getPhonenumber(); |
||||
|
SysUserVo user = userService.selectUserById(userId); |
||||
|
if (ObjectUtil.isNull(user)) { |
||||
|
return R.fail("用户不存在"); |
||||
|
} |
||||
|
// 双轨判重: 走 crypto_remark_two 确定性备份轨道(HSM 密文随机 IV 无法等值比较)
|
||||
|
if (!cryptoSensitiveHsmService.checkPhoneUnique(userId, phonenumber)) { |
||||
|
return R.fail("修改失败,该手机号已被其他账号使用"); |
||||
|
} |
||||
|
// 双轨写入: phonenumber=HSM密文 + crypto_remark_two=AES备份(原子更新; HSM 失败保持原值并返回失败)
|
||||
|
if (!cryptoSensitiveHsmService.savePhoneDualTrack(userId, phonenumber)) { |
||||
|
return R.fail("手机号修改失败,请稍后重试"); |
||||
|
} |
||||
|
// mobile_number 列(市认证扩展字段, AES 形态)同步更新, 避免两手机号字段口径不一致
|
||||
|
String aesPhone = cryptoSensitiveHsmService.normalizePhoneToAes(phonenumber); |
||||
|
if (StringUtils.isNotBlank(aesPhone)) { |
||||
|
sysUserMapper.update(null, Wrappers.<SysUser>lambdaUpdate() |
||||
|
.eq(SysUser::getUserId, userId) |
||||
|
.set(SysUser::getMobileNumber, aesPhone)); |
||||
|
} |
||||
|
// 完整性摘要按库中实际值刷新(摘要口径与库中值一致, 避免完整性校验误报篡改)
|
||||
|
try { |
||||
|
SysUser updated = sysUserMapper.selectById(userId); |
||||
|
cryptoSensitiveHsmService.saveIntegrityHash("sys_user", String.valueOf(userId), |
||||
|
"password,phonenumber,certificateNumber", |
||||
|
updated.getPassword() != null ? updated.getPassword() : "", |
||||
|
updated.getPhonenumber() != null ? updated.getPhonenumber() : "", |
||||
|
updated.getCertificateNumber() != null ? updated.getCertificateNumber() : ""); |
||||
|
} catch (Exception e) { |
||||
|
log.warn("手机号修改后完整性摘要刷新失败, userId={}", userId, e); |
||||
|
} |
||||
|
return R.ok(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据手机号查询用户 |
||||
|
* <p>密评双轨口径: phonenumber 已为 HSM 密文(随机 IV 不可等值比较), |
||||
|
* 归一化后走 crypto_remark_two 备份轨道等值查询, 兼容未双轨化存量行</p> |
||||
|
* |
||||
|
* @param phonenumber 手机号(明文, 11 位) |
||||
|
*/ |
||||
|
@GetMapping("/getUserByPhonenumber") |
||||
|
public R<Map<String, Object>> getUserByPhonenumber( |
||||
|
@NotBlank(message = "手机号不能为空") |
||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") |
||||
|
@RequestParam String phonenumber) { |
||||
|
SysUserVo user = userService.selectUserByPhonenumber(phonenumber); |
||||
|
if (ObjectUtil.isNull(user)) { |
||||
|
return R.fail("未找到该手机号对应的用户"); |
||||
|
} |
||||
|
Map<String, Object> data = new HashMap<>(); |
||||
|
data.put("userId", user.getUserId()); |
||||
|
data.put("userName", user.getUserName()); |
||||
|
data.put("loginName", user.getLoginName()); |
||||
|
data.put("nickName", user.getNickName()); |
||||
|
data.put("deptId", user.getDeptId()); |
||||
|
data.put("status", user.getStatus()); |
||||
|
// 双轨解密展示(备份轨道优先, 兼容明文/AES/HSM 三种存量形态)
|
||||
|
String phone = CommonUtil.decryptPhoneForDisplay(user.getPhonenumber(), user.getCryptoRemarkTwo()); |
||||
|
data.put("phonenumber", phone != null ? phone : ""); |
||||
|
return R.ok(data); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据用户名称 + 手机号查询用户信息 |
||||
|
* <p>密评双轨口径: 手机号条件走 crypto_remark_two 备份轨道等值查询; 用户名称模糊匹配; |
||||
|
* 存量手机号无唯一约束可能重复, 返回全部匹配用户(按更新时间倒序)</p> |
||||
|
* |
||||
|
* @param bo 查询参数(userName + phonenumber, 至少填写一项) |
||||
|
*/ |
||||
|
@PostMapping("/queryUserInfo") |
||||
|
public R<List<Map<String, Object>>> queryUserInfo(@Validated @RequestBody QueryUserInfoBo bo) { |
||||
|
if (StringUtils.isBlank(bo.getUserName()) && StringUtils.isBlank(bo.getPhonenumber())) { |
||||
|
return R.fail("用户名称与手机号至少填写一项"); |
||||
|
} |
||||
|
List<SysUserVo> users = userService.selectUserListByCondition(bo.getUserName(), bo.getPhonenumber()); |
||||
|
if (ObjectUtil.isEmpty(users)) { |
||||
|
return R.fail("未找到符合条件的用户"); |
||||
|
} |
||||
|
List<Map<String, Object>> rows = new ArrayList<>(users.size()); |
||||
|
for (SysUserVo user : users) { |
||||
|
rows.add(buildUserRow(user)); |
||||
|
} |
||||
|
return R.ok(rows); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 组装用户返回数据(手工挑字段, 规避 password/cryptoRemark* 等敏感字段外泄) |
||||
|
*/ |
||||
|
private Map<String, Object> buildUserRow(SysUserVo user) { |
||||
|
Map<String, Object> data = new HashMap<>(); |
||||
|
data.put("userId", user.getUserId()); |
||||
|
data.put("userName", user.getUserName()); |
||||
|
data.put("loginName", user.getLoginName()); |
||||
|
data.put("nickName", user.getNickName()); |
||||
|
data.put("deptId", user.getDeptId()); |
||||
|
data.put("status", user.getStatus()); |
||||
|
data.put("lawCertificateNum", user.getLawCertificateNum()); |
||||
|
// 双轨解密展示(备份轨道优先, 兼容明文/AES/HSM 三种存量形态)
|
||||
|
String phone = CommonUtil.decryptPhoneForDisplay(user.getPhonenumber(), user.getCryptoRemarkTwo()); |
||||
|
data.put("phonenumber", phone != null ? phone : ""); |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.zdxt.enforcementcode.domain.bo; |
||||
|
|
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import jakarta.validation.constraints.NotNull; |
||||
|
import jakarta.validation.constraints.Pattern; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 监督端-更改用户手机号参数 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class ChangePhonenumberBo { |
||||
|
|
||||
|
/** |
||||
|
* 目标用户ID |
||||
|
*/ |
||||
|
@NotNull(message = "用户ID不能为空") |
||||
|
private Long userId; |
||||
|
|
||||
|
/** |
||||
|
* 新手机号(明文, 11位) |
||||
|
*/ |
||||
|
@NotBlank(message = "手机号不能为空") |
||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") |
||||
|
private String phonenumber; |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.zdxt.enforcementcode.domain.bo; |
||||
|
|
||||
|
import jakarta.validation.constraints.Pattern; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 监督端-用户信息组合查询参数 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class QueryUserInfoBo { |
||||
|
|
||||
|
/** |
||||
|
* 用户名称(模糊匹配, 与手机号至少填写一项) |
||||
|
*/ |
||||
|
private String userName; |
||||
|
|
||||
|
/** |
||||
|
* 手机号(明文, 11位, 与用户名称至少填写一项) |
||||
|
*/ |
||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") |
||||
|
private String phonenumber; |
||||
|
} |
||||
Loading…
Reference in new issue