✨ 小程序用户独立为 blade_mp_user,并兼容司机/车辆 OCR 宽松日期
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+42
@@ -53,11 +53,14 @@ import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.system.excel.UserExcel;
|
||||
import org.springblade.system.excel.UserImporter;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
import org.springblade.system.service.IOASyncService;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springblade.system.utils.MpUserAuth;
|
||||
import org.springblade.system.wrapper.UserWrapper;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -79,6 +82,7 @@ import java.util.Map;
|
||||
public class UserController {
|
||||
|
||||
private final IUserService userService;
|
||||
private final IMpUserService mpUserService;
|
||||
private final IOASyncService oaSyncService;
|
||||
|
||||
/**
|
||||
@@ -101,6 +105,10 @@ public class UserController {
|
||||
@Operation(summary = "查看详情", description = "传入id")
|
||||
@GetMapping("/info")
|
||||
public R<UserVO> info(BladeUser user) {
|
||||
if (MpUserAuth.isMpUser(user)) {
|
||||
MpUser mpUser = mpUserService.getById(user.getUserId());
|
||||
return R.data(toMpUserVO(mpUser));
|
||||
}
|
||||
User detail = userService.getById(user.getUserId());
|
||||
return R.data(UserWrapper.build().entityVO(detail));
|
||||
}
|
||||
@@ -252,6 +260,7 @@ public class UserController {
|
||||
* 当前用户设置/重置登录密码(小程序首次设密、短信验证后改密)
|
||||
* <p>
|
||||
* 对外路径:/blade-system/user/password ;网关别名 /blade-user/password 亦可到达。
|
||||
* 小程序用户写入 blade_mp_user,系统用户仍写入 blade_user。
|
||||
*/
|
||||
@PostMapping("/password")
|
||||
@ApiOperationSupport(order = 10)
|
||||
@@ -259,6 +268,9 @@ public class UserController {
|
||||
public R password(BladeUser user,
|
||||
@Parameter(description = "新密码", required = true) @RequestParam String password,
|
||||
@Parameter(description = "确认密码", required = true) @RequestParam String password2) {
|
||||
if (MpUserAuth.isMpUser(user)) {
|
||||
return R.status(mpUserService.setPassword(user.getUserId(), password, password2));
|
||||
}
|
||||
return R.status(userService.setPassword(user.getUserId(), password, password2));
|
||||
}
|
||||
|
||||
@@ -438,4 +450,34 @@ public class UserController {
|
||||
return R.data(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序用户转 UserVO(兼容原 /user/info 响应结构)
|
||||
*/
|
||||
private UserVO toMpUserVO(MpUser mpUser) {
|
||||
if (mpUser == null) {
|
||||
return null;
|
||||
}
|
||||
UserVO userVO = new UserVO();
|
||||
userVO.setId(mpUser.getId());
|
||||
userVO.setTenantId(mpUser.getTenantId());
|
||||
userVO.setCode(mpUser.getCode());
|
||||
userVO.setAccount(mpUser.getAccount());
|
||||
userVO.setName(mpUser.getName());
|
||||
userVO.setRealName(mpUser.getRealName());
|
||||
userVO.setAvatar(mpUser.getAvatar());
|
||||
userVO.setEmail(mpUser.getEmail());
|
||||
userVO.setPhone(mpUser.getPhone());
|
||||
userVO.setBirthday(mpUser.getBirthday());
|
||||
userVO.setSex(mpUser.getSex());
|
||||
userVO.setDeptId(mpUser.getDeptId());
|
||||
userVO.setPostId(mpUser.getPostId());
|
||||
userVO.setRemark(mpUser.getRemark());
|
||||
userVO.setStatus(mpUser.getStatus());
|
||||
userVO.setCreateTime(mpUser.getCreateTime());
|
||||
userVO.setUpdateTime(mpUser.getUpdateTime());
|
||||
// 身份放入 roleName,便于前端兼容展示
|
||||
userVO.setRoleName(mpUser.getIdentities());
|
||||
return userVO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 用户手机号变更(小程序「修改手机号」)
|
||||
* 小程序用户手机号变更(写入 blade_mp_user)
|
||||
* <p>
|
||||
* 对外路径:/blade-system/user/phone/** ;网关别名 /blade-user/phone/** 亦可到达。
|
||||
*
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.feign;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
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.RestController;
|
||||
|
||||
/**
|
||||
* 小程序用户 Feign 实现类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@Hidden
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class MpUserClient implements IMpUserClient {
|
||||
|
||||
private final IMpUserService mpUserService;
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_BY_ID)
|
||||
public R<MpUser> userById(Long userId) {
|
||||
return R.data(mpUserService.getById(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_BY_PHONE)
|
||||
public R<MpUser> userByPhone(String tenantId, String phone) {
|
||||
return R.data(mpUserService.getByPhone(tenantId, phone));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_BY_ACCOUNT)
|
||||
public R<MpUser> userByAccount(String tenantId, String account) {
|
||||
return R.data(mpUserService.getByAccount(tenantId, account));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_INFO_BY_ID)
|
||||
public R<MpUserInfo> userInfoById(Long userId) {
|
||||
return R.data(mpUserService.userInfo(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_INFO_BY_PHONE)
|
||||
public R<MpUserInfo> userInfoByPhone(String tenantId, String phone) {
|
||||
return R.data(mpUserService.userInfoByPhone(tenantId, phone));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_INFO_BY_ACCOUNT)
|
||||
public R<MpUserInfo> userInfoByAccount(String tenantId, String account) {
|
||||
return R.data(mpUserService.userInfoByAccount(tenantId, account));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(SAVE_USER)
|
||||
public R<Boolean> saveUser(@RequestBody MpUser user) {
|
||||
return R.data(mpUserService.submit(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(UPDATE_USER)
|
||||
public R<Boolean> updateUser(@RequestBody MpUser user) {
|
||||
return R.data(mpUserService.updateUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(BIND_OPENID)
|
||||
public R<Boolean> bindOpenId(String tenantId, Long userId, String openid) {
|
||||
return R.data(mpUserService.bindOpenId(tenantId, userId, openid));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(SET_PASSWORD)
|
||||
public R<Boolean> setPassword(Long userId, String password, String password2) {
|
||||
return R.data(mpUserService.setPassword(userId, password, password2));
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
|
||||
/**
|
||||
* 小程序用户 Mapper
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface MpUserMapper extends BaseMapper<MpUser> {
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service;
|
||||
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
|
||||
/**
|
||||
* 小程序用户 服务类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IMpUserService extends BaseService<MpUser> {
|
||||
|
||||
/**
|
||||
* 按租户 + 手机号查询
|
||||
*/
|
||||
MpUser getByPhone(String tenantId, String phone);
|
||||
|
||||
/**
|
||||
* 按租户 + 账号查询
|
||||
*/
|
||||
MpUser getByAccount(String tenantId, String account);
|
||||
|
||||
/**
|
||||
* 按主键组装登录信息
|
||||
*/
|
||||
MpUserInfo userInfo(Long userId);
|
||||
|
||||
/**
|
||||
* 按租户 + 手机号组装登录信息
|
||||
*/
|
||||
MpUserInfo userInfoByPhone(String tenantId, String phone);
|
||||
|
||||
/**
|
||||
* 按租户 + 账号组装登录信息
|
||||
*/
|
||||
MpUserInfo userInfoByAccount(String tenantId, String account);
|
||||
|
||||
/**
|
||||
* 保存小程序用户
|
||||
*/
|
||||
boolean submit(MpUser user);
|
||||
|
||||
/**
|
||||
* 更新小程序用户
|
||||
*/
|
||||
boolean updateUser(MpUser user);
|
||||
|
||||
/**
|
||||
* 绑定/更新 openid
|
||||
*/
|
||||
boolean bindOpenId(String tenantId, Long userId, String openid);
|
||||
|
||||
/**
|
||||
* 当前小程序用户设置密码
|
||||
*/
|
||||
boolean setPassword(Long userId, String password, String password2);
|
||||
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.common.utils.PasswordRuleUtil;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.DigestUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.system.cache.MpUserCache;
|
||||
import org.springblade.system.mapper.MpUserMapper;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.enums.MpUserIdentity;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 小程序用户 服务实现类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> implements IMpUserService {
|
||||
|
||||
@Override
|
||||
public MpUser getByPhone(String tenantId, String phone) {
|
||||
if (Func.isBlank(tenantId) || Func.isBlank(phone)) {
|
||||
return null;
|
||||
}
|
||||
return getOne(Wrappers.<MpUser>lambdaQuery()
|
||||
.eq(MpUser::getTenantId, tenantId)
|
||||
.eq(MpUser::getPhone, phone)
|
||||
.orderByDesc(MpUser::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUser getByAccount(String tenantId, String account) {
|
||||
if (Func.isBlank(tenantId) || Func.isBlank(account)) {
|
||||
return null;
|
||||
}
|
||||
return getOne(Wrappers.<MpUser>lambdaQuery()
|
||||
.eq(MpUser::getTenantId, tenantId)
|
||||
.eq(MpUser::getAccount, account)
|
||||
.orderByDesc(MpUser::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUserInfo userInfo(Long userId) {
|
||||
if (Func.isEmpty(userId)) {
|
||||
return null;
|
||||
}
|
||||
return buildUserInfo(getById(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUserInfo userInfoByPhone(String tenantId, String phone) {
|
||||
return buildUserInfo(getByPhone(tenantId, phone));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUserInfo userInfoByAccount(String tenantId, String account) {
|
||||
return buildUserInfo(getByAccount(tenantId, account));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(MpUser user) {
|
||||
prepare(user);
|
||||
validate(user);
|
||||
MpUser existed = getByPhone(user.getTenantId(), user.getPhone());
|
||||
if (existed != null && Func.isNotEmpty(existed.getId())) {
|
||||
throw new ServiceException("该手机号已存在小程序用户");
|
||||
}
|
||||
boolean saved = save(user);
|
||||
if (saved) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateUser(MpUser user) {
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
throw new ServiceException("小程序用户主键不能为空");
|
||||
}
|
||||
prepare(user);
|
||||
validate(user);
|
||||
MpUser existed = getByPhone(user.getTenantId(), user.getPhone());
|
||||
if (existed != null && Func.isNotEmpty(existed.getId()) && !existed.getId().equals(user.getId())) {
|
||||
throw new ServiceException("该手机号已存在小程序用户");
|
||||
}
|
||||
boolean updated = updateById(user);
|
||||
if (updated) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean bindOpenId(String tenantId, Long userId, String openid) {
|
||||
if (Func.isBlank(tenantId) || Func.isEmpty(userId) || Func.isBlank(openid)) {
|
||||
throw new ServiceException("绑定 openid 参数不完整");
|
||||
}
|
||||
MpUser user = getById(userId);
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
throw new ServiceException("小程序用户不存在");
|
||||
}
|
||||
if (!tenantId.equals(user.getTenantId())) {
|
||||
throw new ServiceException("租户不匹配,无法绑定 openid");
|
||||
}
|
||||
MpUser byOpenId = getOne(Wrappers.<MpUser>lambdaQuery()
|
||||
.eq(MpUser::getTenantId, tenantId)
|
||||
.eq(MpUser::getOpenid, openid)
|
||||
.last("LIMIT 1"));
|
||||
if (byOpenId != null && !byOpenId.getId().equals(userId)) {
|
||||
throw new ServiceException("该微信已绑定其他小程序用户");
|
||||
}
|
||||
MpUser patch = new MpUser();
|
||||
patch.setId(userId);
|
||||
patch.setOpenid(openid);
|
||||
boolean updated = updateById(patch);
|
||||
if (updated) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean setPassword(Long userId, String password, String password2) {
|
||||
if (!Objects.equals(password, password2)) {
|
||||
throw new ServiceException("两次输入密码不一致!");
|
||||
}
|
||||
if (!PasswordRuleUtil.isValid(password)) {
|
||||
throw new ServiceException(PasswordRuleUtil.RULE_MESSAGE);
|
||||
}
|
||||
MpUser user = getById(userId);
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
throw new ServiceException("小程序用户不存在");
|
||||
}
|
||||
MpUser patch = new MpUser();
|
||||
patch.setId(userId);
|
||||
patch.setPassword(DigestUtil.encrypt(password));
|
||||
patch.setUpdateTime(DateUtil.now());
|
||||
boolean updated = updateById(patch);
|
||||
if (updated) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
private MpUserInfo buildUserInfo(MpUser user) {
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
return null;
|
||||
}
|
||||
List<String> identities = MpUserIdentity.split(user.getIdentities());
|
||||
if (identities.isEmpty()) {
|
||||
throw new ServiceException("小程序用户未配置身份,无法登录");
|
||||
}
|
||||
MpUserInfo userInfo = new MpUserInfo();
|
||||
userInfo.setUser(user);
|
||||
userInfo.setIdentities(identities);
|
||||
userInfo.setPermissions(MpUserIdentity.toPermissions(user.getIdentities()));
|
||||
userInfo.setDetail(Kv.create()
|
||||
.set("type", MpUserIdentity.DETAIL_TYPE)
|
||||
.set("identities", user.getIdentities())
|
||||
.set("openid", Func.toStr(user.getOpenid(), "")));
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
private void prepare(MpUser user) {
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
user.setAccount(trimToNull(user.getAccount()));
|
||||
user.setPhone(trimToNull(user.getPhone()));
|
||||
user.setName(trimToNull(user.getName()));
|
||||
user.setRealName(trimToNull(user.getRealName()));
|
||||
user.setAvatar(trimToNull(user.getAvatar()));
|
||||
user.setEmail(trimToNull(user.getEmail()));
|
||||
user.setOpenid(trimToNull(user.getOpenid()));
|
||||
user.setDeptId(trimToNull(user.getDeptId()));
|
||||
user.setPostId(trimToNull(user.getPostId()));
|
||||
user.setRemark(trimToNull(user.getRemark()));
|
||||
user.setIdentities(normalizeIdentities(user.getIdentities()));
|
||||
if (Func.isBlank(user.getAccount()) && Func.isNotBlank(user.getPhone())) {
|
||||
user.setAccount(user.getPhone());
|
||||
}
|
||||
if (Func.isBlank(user.getName()) && Func.isNotBlank(user.getRealName())) {
|
||||
user.setName(user.getRealName());
|
||||
}
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(MpUser user) {
|
||||
if (user == null) {
|
||||
throw new ServiceException("小程序用户不能为空");
|
||||
}
|
||||
if (Func.isBlank(user.getTenantId())) {
|
||||
throw new ServiceException("租户信息不能为空");
|
||||
}
|
||||
if (Func.isBlank(user.getPhone())) {
|
||||
throw new ServiceException("手机号不能为空");
|
||||
}
|
||||
if (Func.isBlank(user.getIdentities())) {
|
||||
throw new ServiceException("小程序身份不能为空");
|
||||
}
|
||||
List<String> identities = MpUserIdentity.split(user.getIdentities());
|
||||
if (identities.isEmpty()) {
|
||||
throw new ServiceException("小程序身份不能为空");
|
||||
}
|
||||
for (String code : identities) {
|
||||
if (MpUserIdentity.of(code) == null) {
|
||||
throw new ServiceException("不支持的小程序身份:" + code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeIdentities(String identities) {
|
||||
List<String> identityList = MpUserIdentity.split(identities);
|
||||
if (identityList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return StringUtil.join(identityList);
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimValue = value.trim();
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
}
|
||||
+51
-39
@@ -18,9 +18,6 @@
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
@@ -28,7 +25,6 @@ package org.springblade.system.service.impl;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.redis.cache.BladeRedis;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
@@ -37,21 +33,21 @@ import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.resource.feign.ISmsClient;
|
||||
import org.springblade.resource.utils.SmsUtil;
|
||||
import org.springblade.system.cache.MpUserCache;
|
||||
import org.springblade.system.pojo.dto.PhoneChangeDTO;
|
||||
import org.springblade.system.pojo.dto.PhoneVerifyDTO;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
import org.springblade.system.service.IUserPhoneService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springblade.system.utils.MpUserAuth;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
|
||||
|
||||
/**
|
||||
* 用户手机号变更服务实现
|
||||
* 小程序用户手机号变更服务实现(写入 blade_mp_user)
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@@ -68,24 +64,22 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
/**
|
||||
* 原手机号已校验凭证(Redis)
|
||||
*/
|
||||
private static final String PHONE_CHANGE_VERIFIED_KEY = "blade:user:phone:change:verified:";
|
||||
private static final String PHONE_CHANGE_VERIFIED_KEY = "blade:mp-user:phone:change:verified:";
|
||||
|
||||
private static final Duration PHONE_CHANGE_VERIFIED_TTL = Duration.ofMinutes(15);
|
||||
|
||||
private static final Pattern MOBILE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
|
||||
|
||||
private final IUserService userService;
|
||||
private final IMpUserService mpUserService;
|
||||
private final ISmsClient smsClient;
|
||||
private final BladeRedis bladeRedis;
|
||||
|
||||
@Override
|
||||
public R sendCode(String phone) {
|
||||
assertMpLogin();
|
||||
String normalizedPhone = normalizePhone(phone);
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
MpUser currentUser = requireCurrentUser(userId);
|
||||
String tenantId = Func.toStr(currentUser.getTenantId(), AuthUtil.getTenantId());
|
||||
boolean isCurrentPhone = StringUtil.equals(normalizedPhone, Func.toStr(currentUser.getPhone()));
|
||||
if (!isCurrentPhone) {
|
||||
@@ -100,16 +94,14 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
|
||||
@Override
|
||||
public boolean verifyOldPhone(PhoneVerifyDTO phoneVerify) {
|
||||
assertMpLogin();
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
String id = Func.toStr(phoneVerify.getId()).trim();
|
||||
String code = Func.toStr(phoneVerify.getCode()).trim();
|
||||
if (StringUtil.isBlank(id) || StringUtil.isBlank(code)) {
|
||||
throw new ServiceException("请先获取并填写验证码");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
MpUser currentUser = requireCurrentUser(userId);
|
||||
String oldPhone = Func.toStr(currentUser.getPhone()).trim();
|
||||
if (StringUtil.isBlank(oldPhone)) {
|
||||
throw new ServiceException("当前账号未绑定手机号");
|
||||
@@ -122,10 +114,8 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean changePhone(PhoneChangeDTO phoneChange) {
|
||||
assertMpLogin();
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
String verified = Func.toStr(bladeRedis.get(PHONE_CHANGE_VERIFIED_KEY + userId));
|
||||
if (!StringUtil.equals(verified, "1")) {
|
||||
throw new ServiceException("请先完成原手机号验证");
|
||||
@@ -136,7 +126,7 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
if (StringUtil.isBlank(id) || StringUtil.isBlank(code)) {
|
||||
throw new ServiceException("请先获取并填写验证码");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
MpUser currentUser = requireCurrentUser(userId);
|
||||
String oldPhone = Func.toStr(currentUser.getPhone()).trim();
|
||||
if (StringUtil.equals(newPhone, oldPhone)) {
|
||||
throw new ServiceException("新手机号不可与当前手机号相同");
|
||||
@@ -145,27 +135,49 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
assertPhoneAvailable(tenantId, newPhone, userId);
|
||||
validateSms(tenantId, id, code, newPhone);
|
||||
|
||||
User updateUser = new User();
|
||||
MpUser updateUser = new MpUser();
|
||||
updateUser.setId(userId);
|
||||
updateUser.setTenantId(tenantId);
|
||||
updateUser.setPhone(newPhone);
|
||||
updateUser.setName(currentUser.getName());
|
||||
updateUser.setRealName(currentUser.getRealName());
|
||||
updateUser.setIdentities(currentUser.getIdentities());
|
||||
updateUser.setSex(currentUser.getSex());
|
||||
updateUser.setDeptId(currentUser.getDeptId());
|
||||
updateUser.setPostId(currentUser.getPostId());
|
||||
updateUser.setAvatar(currentUser.getAvatar());
|
||||
updateUser.setEmail(currentUser.getEmail());
|
||||
updateUser.setRemark(currentUser.getRemark());
|
||||
updateUser.setStatus(currentUser.getStatus());
|
||||
// 账号若等于原手机号,同步更新,保证短信登录可用
|
||||
if (StringUtil.isNotBlank(oldPhone) && StringUtil.equals(oldPhone, Func.toStr(currentUser.getAccount()))) {
|
||||
assertAccountAvailable(tenantId, newPhone, userId);
|
||||
updateUser.setAccount(newPhone);
|
||||
} else {
|
||||
updateUser.setAccount(currentUser.getAccount());
|
||||
}
|
||||
boolean updated = userService.updateById(updateUser);
|
||||
boolean updated = mpUserService.updateUser(updateUser);
|
||||
if (!updated) {
|
||||
throw new ServiceException("手机号修改失败");
|
||||
}
|
||||
bladeRedis.del(PHONE_CHANGE_VERIFIED_KEY + userId);
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
MpUserCache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
private User requireCurrentUser(Long userId) {
|
||||
User user = userService.getById(userId);
|
||||
private void assertMpLogin() {
|
||||
if (Func.isEmpty(AuthUtil.getUserId())) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
if (!MpUserAuth.isMpUser()) {
|
||||
throw new ServiceException("仅支持小程序用户修改手机号");
|
||||
}
|
||||
}
|
||||
|
||||
private MpUser requireCurrentUser(Long userId) {
|
||||
MpUser user = mpUserService.getById(userId);
|
||||
if (user == null) {
|
||||
throw new ServiceException("用户不存在");
|
||||
throw new ServiceException("小程序用户不存在");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
@@ -178,11 +190,11 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
}
|
||||
|
||||
private void assertPhoneAvailable(String tenantId, String phone, Long excludeUserId) {
|
||||
Long phoneCount = userService.count(
|
||||
Wrappers.<User>lambdaQuery()
|
||||
.eq(User::getTenantId, tenantId)
|
||||
.eq(User::getPhone, phone)
|
||||
.ne(User::getId, excludeUserId)
|
||||
Long phoneCount = mpUserService.count(
|
||||
Wrappers.<MpUser>lambdaQuery()
|
||||
.eq(MpUser::getTenantId, tenantId)
|
||||
.eq(MpUser::getPhone, phone)
|
||||
.ne(MpUser::getId, excludeUserId)
|
||||
);
|
||||
if (phoneCount != null && phoneCount > 0L) {
|
||||
throw new ServiceException(StringUtil.format("当前手机 [{}] 已存在!", phone));
|
||||
@@ -190,11 +202,11 @@ public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
}
|
||||
|
||||
private void assertAccountAvailable(String tenantId, String account, Long excludeUserId) {
|
||||
Long accountCount = userService.count(
|
||||
Wrappers.<User>lambdaQuery()
|
||||
.eq(User::getTenantId, tenantId)
|
||||
.eq(User::getAccount, account)
|
||||
.ne(User::getId, excludeUserId)
|
||||
Long accountCount = mpUserService.count(
|
||||
Wrappers.<MpUser>lambdaQuery()
|
||||
.eq(MpUser::getTenantId, tenantId)
|
||||
.eq(MpUser::getAccount, account)
|
||||
.ne(MpUser::getId, excludeUserId)
|
||||
);
|
||||
if (accountCount != null && accountCount > 0L) {
|
||||
throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", account));
|
||||
|
||||
Reference in New Issue
Block a user