11
This commit is contained in:
1 parent
befc75593d
commit
c0fa58d2bc
19 files changed
+219
-2
No files matched your search
@@ -300,6 +300,7 @@ COMMIT;
|
||||
DROP TABLE IF EXISTS `member_user`;
|
||||
CREATE TABLE `member_user` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`user_code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户 Code,大小写敏感',
|
||||
`mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
|
||||
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码',
|
||||
`status` tinyint NOT NULL COMMENT '状态',
|
||||
@@ -325,7 +326,8 @@ CREATE TABLE `member_user` (
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_user_code` (`user_code`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 286 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '会员用户';
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 先执行本脚本增加字段和大小写敏感的唯一索引。
|
||||
-- 再调用 PUT /admin-api/member/user/fill-missing-user-codes,为所有租户的历史用户补齐 Code。
|
||||
ALTER TABLE `member_user`
|
||||
ADD COLUMN `user_code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL
|
||||
COMMENT '用户 Code,大小写敏感' AFTER `id`,
|
||||
ADD UNIQUE KEY `uk_user_code` (`user_code`) USING BTREE;
|
||||
+4
@@ -17,6 +17,10 @@ public class MemberUserRespDTO {
|
||||
* 用户ID
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 用户 Code
|
||||
*/
|
||||
private String userCode;
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
|
||||
+5
@@ -19,6 +19,11 @@ public interface ChatApi {
|
||||
*/
|
||||
ChatLoginRespVO login(@Valid ChatLoginReqVO dto);
|
||||
|
||||
/**
|
||||
* 查询用户在线状态
|
||||
*/
|
||||
List<ChatUserOnLineStatusRespVo> getUserOnlineStatusById(@Valid ChatUserOnLineStatusReqVo dto);
|
||||
|
||||
/**
|
||||
* 创建或者更新聊天用户
|
||||
*/
|
||||
|
||||
+32
@@ -62,6 +62,38 @@ public class ChatApiImpl implements ChatApi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户在线状态
|
||||
*/
|
||||
@Override
|
||||
public List<ChatUserOnLineStatusRespVo> getUserOnlineStatusById(ChatUserOnLineStatusReqVo dto) {
|
||||
ChatApiTool.API api = setApiConfig(ChatApiTool.GET_USER_ONLINE_STATUS_BY_ID);
|
||||
try {
|
||||
String body = JSONUtil.toJsonStr(dto);
|
||||
String res = HttpRequest.post(api.getFullPath()).header(apiCountersign(body)).body(body).execute().body();
|
||||
log.debug("查询用户在线状态,res:{}", res);
|
||||
if (ObjectUtil.isEmpty(res)) {
|
||||
log.error("查询用户在线状态,res为空");
|
||||
return null;
|
||||
}
|
||||
ChatBaseRespVO baseRes = JSONUtil.toBean(res, ChatBaseRespVO.class);
|
||||
if (ObjectUtil.isEmpty(baseRes) || !baseRes.isSuccess()) {
|
||||
log.error("查询用户在线状态,返回错误");
|
||||
return null;
|
||||
}
|
||||
List<ChatUserOnLineStatusRespVo> vo = JSONUtil.toList(baseRes.getData(), ChatUserOnLineStatusRespVo.class);
|
||||
if (ObjectUtil.isEmpty(vo)) {
|
||||
log.error("查询用户在线状态,返回json解析错误");
|
||||
return null;
|
||||
}
|
||||
return vo;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("查询用户在线状态,意外的错误", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或者更新聊天用户
|
||||
*/
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ public class ChatApiTool {
|
||||
public static final API USER_LOGIN = API.builder().summary("用户登录").url("/third/user/login").method(HttpMethod.POST).build();
|
||||
public static final API USER_GET = API.builder().summary("根据userId查询用户详情").url("/third/user/getUserByUserId").method(HttpMethod.POST).build();
|
||||
public static final API USER_SAVE_OR_UPDATE = API.builder().summary("创建或者更新用户").url("/third/user/createOrUpdate").method(HttpMethod.POST).build();
|
||||
public static final API GET_USER_ONLINE_STATUS_BY_ID = API.builder().summary("查询用户在线状态").url("/third/user/getUserOnlineStatusById").method(HttpMethod.POST).build();
|
||||
|
||||
// 朋友圈
|
||||
public static final API FRIEND_ADD = API.builder().summary("添加好友").url("/third/friend/add").method(HttpMethod.POST).build();
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package cn.iocoder.yudao.module.member.client.vo.chat.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ChatUserOnLineStatusReqVo {
|
||||
|
||||
/**
|
||||
* 聊天用户ids
|
||||
*/
|
||||
private List<Long> userIdList;
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.member.client.vo.chat.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "chat用户发送消息resp")
|
||||
public class ChatUserOnLineStatusRespVo {
|
||||
|
||||
@Schema(description = "用户id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否在线")
|
||||
private Boolean online;
|
||||
|
||||
}
|
||||
+3
@@ -43,6 +43,9 @@ public class RecentPageResVo extends RecentDO {
|
||||
@Schema(description = "距离(km)")
|
||||
private Double distance;
|
||||
|
||||
@Schema(description = "是否在线")
|
||||
private Boolean onLine;
|
||||
|
||||
@Data
|
||||
public static class MemberPhoto {
|
||||
|
||||
|
||||
+9
@@ -28,7 +28,9 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.AssertFalse;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -69,6 +71,13 @@ public class MemberUserController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/fill-missing-user-codes")
|
||||
@Operation(summary = "为历史会员补齐用户 Code")
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
public CommonResult<Integer> fillMissingUserCodes() {
|
||||
return success(memberUserService.fillMissingUserCodes());
|
||||
}
|
||||
|
||||
@PutMapping("/update-level")
|
||||
@Operation(summary = "更新会员用户等级")
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update-level')")
|
||||
|
||||
+3
@@ -19,6 +19,9 @@ public class MemberUserRespVO extends MemberUserBaseVO {
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户 Code,6 位数字和大小写字母组合", example = "aB3xY9")
|
||||
private String userCode;
|
||||
|
||||
@Schema(description = "注册 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
|
||||
private String registerIp;
|
||||
|
||||
|
||||
+4
@@ -21,6 +21,10 @@ public class AppMemberUserInfoRespVO {
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户 Code,6 位数字和大小写字母组合", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "aB3xY9")
|
||||
private String userCode;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||
private String nickname;
|
||||
|
||||
|
||||
+5
@@ -43,6 +43,11 @@ public class MemberUserDO extends TenantBaseDO {
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户 Code,6 位数字和大小写字母组合,全局唯一且大小写敏感
|
||||
*/
|
||||
private String userCode;
|
||||
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
|
||||
+18
@@ -26,6 +26,24 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
|
||||
return selectOne(MemberUserDO::getMobile, mobile);
|
||||
}
|
||||
|
||||
default List<Long> selectIdListByEmptyUserCode(int limit) {
|
||||
return selectList(new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.select(MemberUserDO::getId)
|
||||
.and(wrapper -> wrapper.isNull(MemberUserDO::getUserCode)
|
||||
.or().eq(MemberUserDO::getUserCode, ""))
|
||||
.orderByAsc(MemberUserDO::getId)
|
||||
.last("LIMIT " + limit))
|
||||
.stream().map(MemberUserDO::getId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
default int updateUserCodeIfEmpty(Long id, String userCode) {
|
||||
return update(null, new LambdaUpdateWrapper<MemberUserDO>()
|
||||
.set(MemberUserDO::getUserCode, userCode)
|
||||
.eq(MemberUserDO::getId, id)
|
||||
.and(wrapper -> wrapper.isNull(MemberUserDO::getUserCode)
|
||||
.or().eq(MemberUserDO::getUserCode, "")));
|
||||
}
|
||||
|
||||
default List<MemberUserDO> selectListByNicknameLike(String nickname) {
|
||||
return selectList(new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.likeIfPresent(MemberUserDO::getNickname, nickname));
|
||||
|
||||
+26
@@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.member.api.thirdauth.ThirdAuthVo;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.service.TxMapApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusReqVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatUserOnLineStatusRespVo;
|
||||
import cn.iocoder.yudao.module.member.client.vo.txmap.geocoder.TxMapGeocoderReqVO;
|
||||
import cn.iocoder.yudao.module.member.client.vo.txmap.geocoder.TxMapGeocoderRespVO;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.recent.vo.*;
|
||||
@@ -490,6 +492,8 @@ public class RecentServiceImpl implements RecentService {
|
||||
List<RecentPageResVo> resultList = BeanUtils.toBean(recentPageList.getRecords(), RecentPageResVo.class);
|
||||
// 为RecentPageResVo设置Member信息
|
||||
convertRecentPageResVoSetupMember(resultList, isVip);
|
||||
// 远程查询用户是否在线
|
||||
convertRecentPageResVoSetupIsOnline(resultList);
|
||||
// 如果是`新人``女生`模糊`头像`和`用户名`
|
||||
List<MemberRecentSceneEnum> dimList = Arrays.asList(MemberRecentSceneEnum.SQUARE_NEW_PERSON, MemberRecentSceneEnum.SQUARE_FEMALE);
|
||||
// 查询用户tag
|
||||
@@ -785,6 +789,28 @@ public class RecentServiceImpl implements RecentService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为RecentPageResVo设置isOnline信息
|
||||
*/
|
||||
private void convertRecentPageResVoSetupIsOnline(List<RecentPageResVo> list) {
|
||||
if (ObjectUtil.isEmpty(list)) return;
|
||||
List<Long> memberIds = list.stream().map(RecentPageResVo::getMemberId).collect(Collectors.toList());
|
||||
|
||||
ChatUserOnLineStatusReqVo dto = new ChatUserOnLineStatusReqVo();
|
||||
dto.setUserIdList(memberIds);
|
||||
|
||||
List<ChatUserOnLineStatusRespVo> userOnlineList = chatApi.getUserOnlineStatusById(dto);
|
||||
if (ObjectUtil.isEmpty(userOnlineList)) return;
|
||||
Map<Long, ChatUserOnLineStatusRespVo> userOnlineMap = IterUtil.toMap(userOnlineList, ChatUserOnLineStatusRespVo::getId);
|
||||
|
||||
for (RecentPageResVo entity : list) {
|
||||
ChatUserOnLineStatusRespVo userOnLineStatus = userOnlineMap.get(entity.getMemberId());
|
||||
if(ObjectUtil.isNotEmpty(userOnLineStatus)){
|
||||
entity.setOnLine(userOnLineStatus.getOnline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为RecentPageResVo设置OperationTotal信息
|
||||
*/
|
||||
|
||||
+7
@@ -60,6 +60,13 @@ public interface MemberUserService {
|
||||
*/
|
||||
MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal);
|
||||
|
||||
/**
|
||||
* 为所有租户中尚无用户 Code 的历史用户补齐 Code
|
||||
*
|
||||
* @return 本次补齐的用户数量
|
||||
*/
|
||||
int fillMissingUserCodes();
|
||||
|
||||
/**
|
||||
* 更新用户的最后登陆信息
|
||||
*
|
||||
|
||||
+58
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.member.client.service.ChatApi;
|
||||
import cn.iocoder.yudao.module.member.client.vo.chat.user.ChatCreateOrUpdateUserReqVO;
|
||||
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
@@ -35,6 +36,7 @@ import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
|
||||
import cn.iocoder.yudao.module.trade.api.brokerage.TradeBrokerageUserApi;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -61,6 +63,12 @@ import static cn.iocoder.yudao.module.member.enums.MemberErrorCode.*;
|
||||
@Slf4j
|
||||
public class MemberUserServiceImpl implements MemberUserService {
|
||||
|
||||
private static final String USER_CODE_CHARACTERS =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
private static final int USER_CODE_LENGTH = 6;
|
||||
private static final int USER_CODE_GENERATE_MAX_RETRY = 20;
|
||||
private static final int USER_CODE_FILL_BATCH_SIZE = 1000;
|
||||
|
||||
@Resource
|
||||
private MemberUserMapper memberUserMapper;
|
||||
@Resource
|
||||
@@ -132,6 +140,7 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
user.setNickname("用户" + RandomUtil.randomNumbers(6));
|
||||
}
|
||||
memberUserMapper.insert(user);
|
||||
assignUserCode(user);
|
||||
|
||||
// 发送 MQ 消息:用户创建
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@@ -145,6 +154,55 @@ public class MemberUserServiceImpl implements MemberUserService {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public int fillMissingUserCodes() {
|
||||
int fillCount = 0;
|
||||
while (true) {
|
||||
List<Long> userIds = memberUserMapper.selectIdListByEmptyUserCode(USER_CODE_FILL_BATCH_SIZE);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return fillCount;
|
||||
}
|
||||
for (Long userId : userIds) {
|
||||
MemberUserDO user = new MemberUserDO().setId(userId);
|
||||
if (assignUserCode(user)) {
|
||||
fillCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过数据库唯一索引解决并发碰撞;更新方式只修改 user_code,便于安全重试。
|
||||
*/
|
||||
private boolean assignUserCode(MemberUserDO user) {
|
||||
for (int i = 0; i < USER_CODE_GENERATE_MAX_RETRY; i++) {
|
||||
String userCode = generateUserCode();
|
||||
try {
|
||||
if (memberUserMapper.updateUserCodeIfEmpty(user.getId(), userCode) > 0) {
|
||||
user.setUserCode(userCode);
|
||||
return true;
|
||||
}
|
||||
} catch (DuplicateKeyException ex) {
|
||||
log.debug("生成会员用户 Code 发生碰撞,准备重试,userId={}", user.getId());
|
||||
continue;
|
||||
}
|
||||
MemberUserDO currentUser = memberUserMapper.selectById(user.getId());
|
||||
if (currentUser == null) {
|
||||
return false;
|
||||
}
|
||||
if (StrUtil.isNotBlank(currentUser.getUserCode())) {
|
||||
user.setUserCode(currentUser.getUserCode());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("生成会员用户 Code 失败,请稍后重试,userId=" + user.getId());
|
||||
}
|
||||
|
||||
private String generateUserCode() {
|
||||
return RandomUtil.randomString(USER_CODE_CHARACTERS, USER_CODE_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserLogin(Long id, String loginIp) {
|
||||
memberUserMapper.updateById(new MemberUserDO().setId(id)
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS "member_user"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
|
||||
"user_code" varchar(6) NULL COMMENT '用户 Code',
|
||||
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
|
||||
"name" varchar(30) NULL COMMENT '真实名字',
|
||||
sex tinyint null comment '性别',
|
||||
@@ -25,7 +26,8 @@ CREATE TABLE IF NOT EXISTS "member_user"
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
"deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
|
||||
"tenant_id" bigint not null default '0',
|
||||
PRIMARY KEY ("id")
|
||||
PRIMARY KEY ("id"),
|
||||
UNIQUE ("user_code")
|
||||
) COMMENT '会员表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "member_address" (
|
||||
|
||||
@@ -234,6 +234,7 @@ pf4j:
|
||||
|
||||
chat:
|
||||
base-url: http://im-platform.htysapp.com
|
||||
# base-url: http://127.0.0.1:8888
|
||||
app-key: 4H2HkBLygMsxaQKO
|
||||
app-secret: JIizm84av3TK5xaQdsng7X5mqLPjbeQ0
|
||||
|
||||
|
||||
Reference in new issue
Block a user