From 9a6551aebd6b523439beda5c642371998c5289f6 Mon Sep 17 00:00:00 2001
From: 刘帅 <rr.iii@qq.com>
Date: 星期三, 30 九月 2026 09:57:53 +0800
Subject: [PATCH] 门店手机号展示问题
---
cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/system/UserMgrController.java | 91 ++++++++++++++++++++++++++++++++++++---------
1 files changed, 72 insertions(+), 19 deletions(-)
diff --git a/cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/system/UserMgrController.java b/cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/system/UserMgrController.java
index 373d8b5..c62f9dd 100644
--- a/cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/system/UserMgrController.java
+++ b/cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/system/UserMgrController.java
@@ -1,6 +1,7 @@
package com.dsh.guns.modular.system.controller.system;
import cn.hutool.crypto.SecureUtil;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -152,9 +153,16 @@
User user = this.userService.getById(userId);
Role role = roleService.getById(user.getRoleid());
Dept dept = deptService.getById(user.getDeptid());
+ // 兼容历史账号:部分账号的登录手机号仅保存在 account 字段。
+ // 个人资料页必须能回显该绑定手机号,避免前端提交空值。
+ if (StringUtils.isEmpty(user.getPhone()) && !StringUtils.isEmpty(user.getAccount())) {
+ user.setPhone(user.getAccount());
+ }
user.setPhone(maskPhone(user.getPhone()));
user.setIdCard(maskIdCard(user.getIdCard()));
- model.addAttribute(user);
+ // 显式指定模型名称和身份证回显字段,避免模板按隐式参数名解析失败。
+ model.addAttribute("user", user);
+ model.addAttribute("idCard", user.getIdCard());
if(role==null){
model.addAttribute("roleName", "");
}else {
@@ -174,6 +182,11 @@
}
roleLambdaQueryWrapper.eq(Role::getObjType,UserExt.getUser().getObjectType());
List<Role> list = roleService.list(roleLambdaQueryWrapper);
+ // 员工可能是由平台账号创建、后续归属到门店的,原角色不一定属于当前登录人的角色范围。
+ // 编辑时仍需保留该角色作为可选项,避免下拉框无法回显。
+ if (role != null && list.stream().noneMatch(item -> Objects.equals(item.getId(), role.getId()))) {
+ list.add(role);
+ }
model.addAttribute("list",list);
model.addAttribute("language",UserExt.getLanguage());
// 部门列表
@@ -208,6 +221,11 @@
User user = this.userService.getById(userId);
Role role = roleService.getById(user.getRoleid());
Dept dept = deptService.getById(user.getDeptid());
+ // 兼容历史账号:部分账号的登录手机号仅保存在 account 字段。
+ // 个人资料页必须能回显该绑定手机号,避免前端提交空值。
+ if (StringUtils.isEmpty(user.getPhone()) && !StringUtils.isEmpty(user.getAccount())) {
+ user.setPhone(user.getAccount());
+ }
user.setPhone(maskPhone(user.getPhone()));
user.setIdCard(maskIdCard(user.getIdCard()));
model.addAttribute(user);
@@ -331,6 +349,7 @@
@BussinessLog(value = "添加管理员", key = "account", dict = UserDict.class)
@Permission(Const.ADMIN_NAME)
@ResponseBody
+ @Transactional(rollbackFor = Exception.class)
public Object add(@Valid UserDto user, BindingResult result) {
if (result.hasErrors()) {
throw new GunsException(BizExceptionEnum.REQUEST_NULL);
@@ -340,7 +359,10 @@
if (StringUtils.isEmpty(user.getPhone())) {
return 5002;
}
- User theUser = userService.getOne(new QueryWrapper<User>().eq("phone", user.getPhone()));
+ // 已逻辑删除的员工(status=3)不占用手机号,可用同一手机号重新创建员工。
+ User theUser = userService.getOne(new QueryWrapper<User>()
+ .eq("phone", user.getPhone())
+ .ne("status", ManagerStatus.DELETED.getCode()));
if (theUser != null || userService.getByAccount(user.getPhone()) != null){
return 5001;
}
@@ -404,13 +426,7 @@
objectUser.setVersion(UserExt.getUser().getId());
this.userService.save(objectUser);
if (!assignedStores.isEmpty()) {
- userMapper.deleteStoreRelations(objectUser.getId());
- userMapper.insertStoreRelations(objectUser.getId(), assignedStores.stream().map(TStore::getId).collect(Collectors.toList()));
- // 各所选门店同步负责人账号;旧系统仍可通过 storeStaffId 查询主负责人。
- for (TStore store : assignedStores) {
- store.setStoreStaffId(objectUser.getId());
- storeService.updateById(store);
- }
+ replaceStoreRelations(objectUser.getId(), assignedStores);
}
return SUCCESS_TIP;
}
@@ -430,10 +446,13 @@
}
User oldUser = userService.getById(user.getId());
// 判断是否更改电话
- if (user.getPhone() != null && user.getPhone().contains("*")) {
+ if (StringUtils.isEmpty(user.getPhone()) || user.getPhone().contains("*")) {
// The edit view displays a masked phone. Treat an unchanged masked
- // value as "keep original", never as a value to persist.
- } else if (!oldUser.getPhone().equals(user.getPhone())){
+ // value, or an omitted field, as "keep original", never as a
+ // value to persist. Some edit forms do not submit disabled or
+ // hidden fields; allowing an empty value through would erase the
+ // stored phone after deployment.
+ } else if (!Objects.equals(oldUser.getPhone(), user.getPhone())) {
User phone = userService.getOne(new QueryWrapper<User>().eq("phone", user.getPhone()));
if (phone == null){
oldUser.setPhone(user.getPhone());
@@ -444,7 +463,11 @@
oldUser.setName(user.getName());
oldUser.setBirthday(user.getBirthday());
oldUser.setDeptid(user.getDeptid());
- if (user.getIdCard() == null || !user.getIdCard().contains("*")) oldUser.setIdCard(user.getIdCard());
+ // 身份证号为空说明本次表单没有提交该字段,不能覆盖数据库中的历史值;
+ // 脱敏值同样只用于回显,不能写回数据库。
+ if (!StringUtils.isEmpty(user.getIdCard()) && !user.getIdCard().contains("*")) {
+ oldUser.setIdCard(user.getIdCard());
+ }
oldUser.setSex(user.getSex());
oldUser.setEmail(user.getEmail());
if (user.getAvatar()!=null && (!user.getAvatar().equals("")))
@@ -452,20 +475,42 @@
if (user.getStoreIds() != null) {
List<TStore> assignedStores = validateStores(user.getStoreIds());
if (assignedStores == null) return ResultUtil.error("所选门店不存在、已停用或不在当前账号管理范围内");
- userMapper.deleteStoreRelations(oldUser.getId());
+ replaceStoreRelations(oldUser.getId(), assignedStores);
if (!assignedStores.isEmpty()) {
oldUser.setObjectType(3);
oldUser.setObjectId(assignedStores.get(0).getId());
oldUser.setRoleid("3");
- userMapper.insertStoreRelations(oldUser.getId(), assignedStores.stream().map(TStore::getId).collect(Collectors.toList()));
- for (TStore store : assignedStores) {
- store.setStoreStaffId(oldUser.getId());
- storeService.updateById(store);
- }
+ } else if (Integer.valueOf(3).equals(oldUser.getObjectType())) {
+ // 清除旧字段关联,避免已移除的首个门店仍被当作该员工所属门店。
+ oldUser.setObjectId(null);
}
+ }
+ // 门店关联会维护员工归属信息,但不能覆盖编辑页明确选择的角色。
+ // 必须在关联处理后写入,否则有门店的员工会被上面的兼容逻辑强制改回角色 3。
+ // 个人资料页不提供 roleid_ 回显控件时,旧版前端可能提交字符串
+ // "undefined";该值绝不能覆盖 sys_user.roleid。
+ if (isValidRoleId(user.getRoleid())) {
+ oldUser.setRoleid(user.getRoleid());
+ // 仅内置平台管理员(1)和门店管理员(3)切换数据归属类型;
+ // 其他自定义角色只控制菜单权限,不改变账号归属。objectId 始终保留。
+ syncObjectTypeWithBuiltInRole(oldUser, user.getRoleid());
}
this.userService.updateById(oldUser);
return SUCCESS_TIP;
+ }
+
+ private boolean isValidRoleId(String roleId) {
+ return !StringUtils.isEmpty(roleId)
+ && !"undefined".equalsIgnoreCase(roleId)
+ && !"null".equalsIgnoreCase(roleId);
+ }
+
+ private void syncObjectTypeWithBuiltInRole(User user, String roleId) {
+ if ("1".equals(roleId)) {
+ user.setObjectType(1);
+ } else if ("3".equals(roleId)) {
+ user.setObjectType(3);
+ }
}
private String maskPhone(String value) {
@@ -498,6 +543,14 @@
return stores;
}
+ /** 每条关联记录单独生成雪花主键,避免 MyBatis foreach 复用同一个绑定变量。 */
+ private void replaceStoreRelations(Integer userId, List<TStore> stores) {
+ userMapper.deleteStoreRelations(userId);
+ for (TStore store : stores) {
+ userMapper.insertStoreRelation(IdWorker.getId(), userId, store.getId());
+ }
+ }
+
/**
* 删除管理员(逻辑删除)
* Delete administrator (logical deletion)
--
Gitblit v1.7.1