mirror of
https://github.com/zongzibinbin/MallChat.git
synced 2026-03-19 09:53:45 +08:00
项目基础搭建
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package com.abin.mallchat.common.user.dao;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.ItemConfig;
|
||||
import com.abin.mallchat.common.user.mapper.ItemConfigMapper;
|
||||
import com.abin.mallchat.common.user.service.IItemConfigService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 功能物品配置表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Service
|
||||
public class ItemConfigDao extends ServiceImpl<ItemConfigMapper, ItemConfig> {
|
||||
|
||||
public List<ItemConfig> getByType(Integer type) {
|
||||
return lambdaQuery().eq(ItemConfig::getType, type).list();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.abin.mallchat.common.user.dao;
|
||||
|
||||
import com.abin.mallchat.common.common.domain.enums.YesOrNoEnum;
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import com.abin.mallchat.common.user.domain.entity.UserBackpack;
|
||||
import com.abin.mallchat.common.user.mapper.UserBackpackMapper;
|
||||
import com.abin.mallchat.common.user.service.IUserBackpackService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户背包表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Service
|
||||
public class UserBackpackDao extends ServiceImpl<UserBackpackMapper, UserBackpack> {
|
||||
|
||||
public Integer getCountByValidItemId(Long uid, Long itemId) {
|
||||
LambdaQueryWrapper<UserBackpack> wrapper = new QueryWrapper<UserBackpack>().lambda()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus());
|
||||
return count(wrapper);
|
||||
}
|
||||
|
||||
public UserBackpack getFirstValidItem(Long uid, Long itemId) {
|
||||
LambdaQueryWrapper<UserBackpack> wrapper = new QueryWrapper<UserBackpack>().lambda()
|
||||
.eq(UserBackpack::getUid, uid)
|
||||
.eq(UserBackpack::getItemId, itemId)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.last("limit 1");
|
||||
return getOne(wrapper);
|
||||
}
|
||||
|
||||
public boolean invalidItem(Long itemId) {
|
||||
UserBackpack update = new UserBackpack();
|
||||
update.setItemId(itemId);
|
||||
update.setStatus(YesOrNoEnum.YES.getStatus());
|
||||
return updateById(update);
|
||||
}
|
||||
|
||||
public List<UserBackpack> getByItemIds(Long uid, List<Long> itemIds) {
|
||||
return lambdaQuery().eq(UserBackpack::getUid, uid)
|
||||
.in(UserBackpack::getItemId, itemIds)
|
||||
.eq(UserBackpack::getStatus, YesOrNoEnum.NO.getStatus())
|
||||
.list();
|
||||
}
|
||||
|
||||
public UserBackpack getByIdp(String idempotent) {
|
||||
return lambdaQuery().eq(UserBackpack::getIdempotent, idempotent).one();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.abin.mallchat.common.user.dao;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import com.abin.mallchat.common.user.mapper.UserMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Service
|
||||
public class UserDao extends ServiceImpl<UserMapper, User> {
|
||||
|
||||
public User getByOpenId(String openId) {
|
||||
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda().eq(User::getOpenId, openId);
|
||||
return getOne(wrapper);
|
||||
}
|
||||
|
||||
public void modifyName(Long uid, String name) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setName(name);
|
||||
updateById(update);
|
||||
}
|
||||
|
||||
public void wearingBadge(Long uid, Long badgeId) {
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setItemId(badgeId);
|
||||
updateById(update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.abin.mallchat.common.user.domain.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-04-18
|
||||
*/
|
||||
@Data
|
||||
public class IpResult<T> implements Serializable {
|
||||
@ApiModelProperty("错误码")
|
||||
private Integer code;
|
||||
@ApiModelProperty("错误消息")
|
||||
private String msg;
|
||||
@ApiModelProperty("返回对象")
|
||||
private T data;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return Objects.nonNull(this.code) && this.code == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.abin.mallchat.common.user.domain.entity;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户ip信息
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IpDetail implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
//注册时的ip
|
||||
private String ip;
|
||||
//最新登录的ip
|
||||
private String isp;
|
||||
private String isp_id;
|
||||
private String city;
|
||||
private String city_id;
|
||||
private String country;
|
||||
private String country_id;
|
||||
private String region;
|
||||
private String region_id;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.abin.mallchat.common.user.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户ip信息
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class IpInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
//注册时的ip
|
||||
private String createIp;
|
||||
//注册时的ip详情
|
||||
private IpDetail createIpDetail;
|
||||
//最新登录的ip
|
||||
private String updateIp;
|
||||
//最新登录的ip详情
|
||||
private IpDetail updateIpDetail;
|
||||
|
||||
public void refreshIp(String ip) {
|
||||
if (Objects.isNull(ip)) {
|
||||
return;
|
||||
}
|
||||
updateIp = ip;
|
||||
if (createIp == null) {
|
||||
createIp = ip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要刷新的ip,这里判断更新ip就够,初始化的时候ip也是相同的,只需要设置的时候多设置进去就行
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String needRefreshIp() {
|
||||
boolean notNeedRefresh = Optional.ofNullable(updateIpDetail)
|
||||
.map(IpDetail::getIp)
|
||||
.filter(ip -> ip.equals(updateIp))
|
||||
.isPresent();
|
||||
return notNeedRefresh ? null : updateIp;
|
||||
}
|
||||
|
||||
public void refreshIpDetail(IpDetail ipDetail) {
|
||||
if (Objects.equals(createIp, ipDetail)) {
|
||||
createIpDetail = ipDetail;
|
||||
}
|
||||
if (Objects.equals(updateIp, ipDetail)) {
|
||||
updateIpDetail = ipDetail;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.abin.mallchat.common.user.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 功能物品配置表
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("item_config")
|
||||
public class ItemConfig implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId("id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 物品类型 1改名卡 2徽章
|
||||
*/
|
||||
@TableField("type")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 物品图片
|
||||
*/
|
||||
@TableField("img")
|
||||
private String img;
|
||||
|
||||
/**
|
||||
* 物品功能描述
|
||||
*/
|
||||
@TableField("`describe`")
|
||||
private String describe;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.abin.mallchat.common.user.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.*;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("user")
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
@TableField("avatar")
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 性别 1为男性,2为女性
|
||||
*/
|
||||
@TableField("sex")
|
||||
private Integer sex;
|
||||
|
||||
/**
|
||||
* 微信openid用户标识
|
||||
*/
|
||||
@TableField("open_id")
|
||||
private String openId;
|
||||
|
||||
/**
|
||||
* 最后上下线时间
|
||||
*/
|
||||
@TableField("last_opt_time")
|
||||
private Date lastOptTime;
|
||||
|
||||
/**
|
||||
* 最后上下线时间
|
||||
*/
|
||||
@TableField(value = "ip_info", typeHandler = JacksonTypeHandler.class)
|
||||
private IpInfo ipInfo;
|
||||
|
||||
/**
|
||||
* 佩戴的徽章id
|
||||
*/
|
||||
@TableField("item_id")
|
||||
private Long itemId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private Date updateTime;
|
||||
|
||||
public IpInfo getIpInfo() {
|
||||
if (ipInfo == null) {
|
||||
ipInfo = new IpInfo();
|
||||
}
|
||||
return ipInfo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.abin.mallchat.common.user.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.*;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户背包表
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("user_backpack")
|
||||
public class UserBackpack implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* uid
|
||||
*/
|
||||
@TableField("uid")
|
||||
private Long uid;
|
||||
|
||||
/**
|
||||
* 物品id
|
||||
*/
|
||||
@TableField("item_id")
|
||||
private Long itemId;
|
||||
|
||||
/**
|
||||
* 使用状态 0.未失效 1失效
|
||||
*/
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 幂等号
|
||||
*/
|
||||
@TableField("idempotent")
|
||||
private String idempotent;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.abin.mallchat.common.user.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Description: ws前端请求类型枚举
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-03-19
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum ChatActiveStatusEnum {
|
||||
ONLINE(1, "在线"),
|
||||
OFFLINE(2, "离线"),
|
||||
;
|
||||
|
||||
private final Integer status;
|
||||
private final String desc;
|
||||
|
||||
private static Map<Integer, ChatActiveStatusEnum> cache;
|
||||
|
||||
static {
|
||||
cache = Arrays.stream(ChatActiveStatusEnum.values()).collect(Collectors.toMap(ChatActiveStatusEnum::getStatus, Function.identity()));
|
||||
}
|
||||
|
||||
public static ChatActiveStatusEnum of(Integer type) {
|
||||
return cache.get(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.abin.mallchat.common.user.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Description: 物品枚举
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-03-19
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum ItemEnum {
|
||||
MODIFY_NAME_CARD(1L, ItemTypeEnum.MODIFY_NAME_CARD, "改名卡"),
|
||||
LIKE_BADGE(2L, ItemTypeEnum.BADGE, "爆赞徽章"),
|
||||
REG_TOP10_BADGE(2L, ItemTypeEnum.BADGE, "爆赞徽章"),
|
||||
REG_TOP100_BADGE(2L, ItemTypeEnum.BADGE, "爆赞徽章"),
|
||||
;
|
||||
|
||||
private final Long id;
|
||||
private final ItemTypeEnum typeEnum;
|
||||
private final String desc;
|
||||
|
||||
private static Map<Long, ItemEnum> cache;
|
||||
|
||||
static {
|
||||
cache = Arrays.stream(ItemEnum.values()).collect(Collectors.toMap(ItemEnum::getId, Function.identity()));
|
||||
}
|
||||
|
||||
public static ItemEnum of(Integer type) {
|
||||
return cache.get(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.abin.mallchat.common.user.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Description: 物品枚举
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-03-19
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum ItemTypeEnum {
|
||||
MODIFY_NAME_CARD(1, "改名卡"),
|
||||
BADGE(2, "徽章"),
|
||||
;
|
||||
|
||||
private final Integer type;
|
||||
private final String desc;
|
||||
|
||||
private static Map<Integer, ItemTypeEnum> cache;
|
||||
|
||||
static {
|
||||
cache = Arrays.stream(ItemTypeEnum.values()).collect(Collectors.toMap(ItemTypeEnum::getType, Function.identity()));
|
||||
}
|
||||
|
||||
public static ItemTypeEnum of(Integer type) {
|
||||
return cache.get(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.abin.mallchat.common.user.mapper;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.ItemConfig;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 功能物品配置表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
public interface ItemConfigMapper extends BaseMapper<ItemConfig> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.abin.mallchat.common.user.mapper;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.UserBackpack;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户背包表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
public interface UserBackpackMapper extends BaseMapper<UserBackpack> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.abin.mallchat.common.user.mapper;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.abin.mallchat.common.user.service;
|
||||
|
||||
import com.abin.mallchat.common.user.domain.entity.ItemConfig;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 功能物品配置表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
public interface IItemConfigService extends IService<ItemConfig> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.abin.mallchat.common.user.service;
|
||||
|
||||
import com.abin.mallchat.common.common.domain.enums.IdempotentEnum;
|
||||
import com.abin.mallchat.common.user.domain.entity.UserBackpack;
|
||||
import com.abin.mallchat.common.user.domain.enums.ItemEnum;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户背包表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
public interface IUserBackpackService {
|
||||
|
||||
|
||||
/**
|
||||
* 用户获取一个物品
|
||||
* @param uid 用户id
|
||||
* @param itemId 物品id
|
||||
* @param idempotentEnum 幂等类型
|
||||
* @param businessId 上层业务发送的唯一标识
|
||||
*/
|
||||
void acquireItem(Long uid, Long itemId, IdempotentEnum idempotentEnum, String businessId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.abin.mallchat.common.user.service;
|
||||
|
||||
public interface IpService {
|
||||
/**
|
||||
* 异步更新用户ip详情
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
void refreshIpDetailAsync(Long id);
|
||||
}
|
||||
42
mallchat-common/src/main/java/com/abin/mallchat/common/user/service/cache/ItemCache.java
vendored
Normal file
42
mallchat-common/src/main/java/com/abin/mallchat/common/user/service/cache/ItemCache.java
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.abin.mallchat.common.user.service.cache;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||
import com.abin.mallchat.common.common.domain.vo.request.CursorPageBaseReq;
|
||||
import com.abin.mallchat.common.common.domain.vo.response.CursorPageBaseResp;
|
||||
import com.abin.mallchat.common.common.utils.CursorUtils;
|
||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||
import com.abin.mallchat.common.user.dao.ItemConfigDao;
|
||||
import com.abin.mallchat.common.user.dao.UserDao;
|
||||
import com.abin.mallchat.common.user.domain.entity.ItemConfig;
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Description: 用户相关缓存
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-03-27
|
||||
*/
|
||||
@Component
|
||||
public class ItemCache {//todo 多级缓存
|
||||
|
||||
@Autowired
|
||||
private ItemConfigDao itemConfigDao;
|
||||
|
||||
@Cacheable(cacheNames = "item", key = "'itemsByType:'+#type")
|
||||
public List<ItemConfig> getByType(Integer type) {
|
||||
return itemConfigDao.getByType(type);
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "item", key = "'item:'+#itemId")
|
||||
public ItemConfig getById(Long itemId) {
|
||||
return itemConfigDao.getById(itemId);
|
||||
}
|
||||
}
|
||||
112
mallchat-common/src/main/java/com/abin/mallchat/common/user/service/cache/UserCache.java
vendored
Normal file
112
mallchat-common/src/main/java/com/abin/mallchat/common/user/service/cache/UserCache.java
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.abin.mallchat.common.user.service.cache;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import com.abin.mallchat.common.common.constant.RedisKey;
|
||||
import com.abin.mallchat.common.common.domain.vo.request.CursorPageBaseReq;
|
||||
import com.abin.mallchat.common.common.domain.vo.response.CursorPageBaseResp;
|
||||
import com.abin.mallchat.common.common.utils.CursorUtils;
|
||||
import com.abin.mallchat.common.common.utils.RedisUtils;
|
||||
import com.abin.mallchat.common.user.dao.UserDao;
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Description: 用户相关缓存
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-03-27
|
||||
*/
|
||||
@Component
|
||||
public class UserCache {
|
||||
|
||||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
@Autowired
|
||||
private CursorUtils cursorUtils;
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
public Long getOnlineNum() {
|
||||
String onlineKey = RedisKey.getKey(RedisKey.ONLINE_UID_ZET);
|
||||
return redisUtils.zCard(onlineKey);
|
||||
}
|
||||
|
||||
public Long getOfflineNum() {
|
||||
String offlineKey = RedisKey.getKey(RedisKey.OFFLINE_UID_ZET);
|
||||
return redisUtils.zCard(offlineKey);
|
||||
}
|
||||
|
||||
//用户上线
|
||||
public void online(Long uid, Date optTime) {
|
||||
String onlineKey = RedisKey.getKey(RedisKey.ONLINE_UID_ZET);
|
||||
String offlineKey = RedisKey.getKey(RedisKey.OFFLINE_UID_ZET);
|
||||
//移除离线表
|
||||
redisUtils.zRemove(offlineKey, uid);
|
||||
//更新上线表
|
||||
redisUtils.zAdd(onlineKey, uid, optTime.getTime());
|
||||
}
|
||||
|
||||
//用户下线
|
||||
public void offline(Long uid, Date optTime) {
|
||||
String onlineKey = RedisKey.getKey(RedisKey.ONLINE_UID_ZET);
|
||||
String offlineKey = RedisKey.getKey(RedisKey.OFFLINE_UID_ZET);
|
||||
//移除上线线表
|
||||
redisUtils.zRemove(onlineKey, uid);
|
||||
//更新上线表
|
||||
redisUtils.zAdd(offlineKey, uid, optTime.getTime());
|
||||
}
|
||||
|
||||
public CursorPageBaseResp<Pair<Long, Double>> getOnlineCursorPage(CursorPageBaseReq pageBaseReq) {
|
||||
return cursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.ONLINE_UID_ZET), Long::parseLong);
|
||||
}
|
||||
|
||||
public CursorPageBaseResp<Pair<Long, Double>> getOfflineCursorPage(CursorPageBaseReq pageBaseReq) {
|
||||
return cursorUtils.getCursorPageByRedis(pageBaseReq, RedisKey.getKey(RedisKey.OFFLINE_UID_ZET), Long::parseLong);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息,盘路缓存模式
|
||||
*
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
public User getUserInfo(Long uid) {//todo 后期做二级缓存
|
||||
return getUserInfoBatch(Collections.singleton(uid)).get(uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息,盘路缓存模式
|
||||
*
|
||||
* @param uids
|
||||
* @return
|
||||
*/
|
||||
public Map<Long, User> getUserInfoBatch(Set<Long> uids) {
|
||||
List<String> keys = uids.stream().map(a -> RedisKey.getKey(RedisKey.USER_INFO_STRING, a)).collect(Collectors.toList());
|
||||
List<User> mget = redisUtils.mget(keys, User.class);
|
||||
Map<Long, User> map = mget.stream().collect(Collectors.toMap(User::getId, Function.identity()));
|
||||
//还需要load更新的uid
|
||||
List<Long> needLoadUidList = uids.stream().filter(a -> !map.containsKey(a)).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(needLoadUidList)) {
|
||||
List<User> needLoadUserList = userDao.listByIds(needLoadUidList);
|
||||
Map<String, User> redisMap = needLoadUserList.stream().collect(Collectors.toMap(a -> RedisKey.getKey(RedisKey.USER_INFO_STRING, a.getId()), Function.identity()));
|
||||
redisUtils.mset(redisMap, 5 * 60);
|
||||
map.putAll(needLoadUserList.stream().collect(Collectors.toMap(User::getId, Function.identity())));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public void delUserInfo(Long uid) {
|
||||
String key = RedisKey.getKey(RedisKey.USER_INFO_STRING, uid);
|
||||
redisUtils.del(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.abin.mallchat.common.user.service.impl;
|
||||
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.core.thread.NamedThreadFactory;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.abin.mallchat.common.common.domain.vo.response.ApiResult;
|
||||
import com.abin.mallchat.common.user.dao.UserDao;
|
||||
import com.abin.mallchat.common.user.domain.dto.IpResult;
|
||||
import com.abin.mallchat.common.user.domain.entity.IpDetail;
|
||||
import com.abin.mallchat.common.user.domain.entity.IpInfo;
|
||||
import com.abin.mallchat.common.user.domain.entity.User;
|
||||
import com.abin.mallchat.common.user.service.IpService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* Description: ip
|
||||
* Author: <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* Date: 2023-04-18
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class IpServiceImpl implements IpService, DisposableBean {
|
||||
private static ExecutorService executor = new ThreadPoolExecutor(1, 1,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(500), new NamedThreadFactory("refresh-ipDetail", false));
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@Override
|
||||
public void refreshIpDetailAsync(Long uid) {
|
||||
executor.execute(() -> {
|
||||
User user = userDao.getById(uid);
|
||||
IpInfo ipInfo = user.getIpInfo();
|
||||
String ip = ipInfo.needRefreshIp();
|
||||
if (StrUtil.isBlank(ip)) {
|
||||
return;
|
||||
}
|
||||
IpDetail ipDetail = TryGetIpDetailOrNullTreeTimes(ip);
|
||||
if (Objects.nonNull(ipDetail)) {
|
||||
ipInfo.refreshIpDetail(ipDetail);
|
||||
User update = new User();
|
||||
update.setId(uid);
|
||||
update.setIpInfo(ipInfo);
|
||||
userDao.updateById(update);
|
||||
} else {
|
||||
log.error("get ip detail fail ip:{},uid:{}", ip, uid);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static IpDetail TryGetIpDetailOrNullTreeTimes(String ip) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
IpDetail ipDetail = getIpDetailOrNull(ip);
|
||||
if (Objects.nonNull(ipDetail)) {
|
||||
return ipDetail;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IpDetail getIpDetailOrNull(String ip) {
|
||||
String body = HttpUtil.get("https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc");
|
||||
try {
|
||||
IpResult<IpDetail> result = JSONUtil.toBean(body, new TypeReference<IpResult<IpDetail>>() {
|
||||
}, false);
|
||||
if (result.isSuccess()) {
|
||||
return result.getData();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//测试耗时结果 100次查询总耗时约100s,平均一次成功查询需要1s,可以接受
|
||||
//第99次成功,目前耗时:99545ms
|
||||
public static void main(String[] args) {
|
||||
Date begin = new Date();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int finalI = i;
|
||||
executor.execute(() -> {
|
||||
IpDetail ipDetail = TryGetIpDetailOrNullTreeTimes("113.90.36.126");
|
||||
if (Objects.nonNull(ipDetail)) {
|
||||
Date date = new Date();
|
||||
System.out.println(String.format("第%d次成功,目前耗时:%dms", finalI, (date.getTime() - begin.getTime())));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws InterruptedException {
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {//最多等30秒,处理不完就拉倒
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Timed out while waiting for executor [{}] to terminate", executor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.abin.mallchat.common.user.service.impl;
|
||||
|
||||
import com.abin.mallchat.common.common.domain.enums.IdempotentEnum;
|
||||
import com.abin.mallchat.common.common.domain.enums.YesOrNoEnum;
|
||||
import com.abin.mallchat.common.user.dao.ItemConfigDao;
|
||||
import com.abin.mallchat.common.user.dao.UserBackpackDao;
|
||||
import com.abin.mallchat.common.user.domain.entity.ItemConfig;
|
||||
import com.abin.mallchat.common.user.domain.entity.UserBackpack;
|
||||
import com.abin.mallchat.common.user.domain.enums.ItemTypeEnum;
|
||||
import com.abin.mallchat.common.user.service.IUserBackpackService;
|
||||
import com.abin.mallchat.common.user.service.cache.ItemCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户背包表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="https://github.com/zongzibinbin">abin</a>
|
||||
* @since 2023-03-19
|
||||
*/
|
||||
@Service
|
||||
public class UserBackpackServiceImpl implements IUserBackpackService {
|
||||
@Autowired
|
||||
private UserBackpackDao userBackpackDao;
|
||||
@Autowired
|
||||
private ItemConfigDao itemConfigDao;
|
||||
@Autowired
|
||||
private ItemCache itemCache;
|
||||
|
||||
@Override
|
||||
public void acquireItem(Long uid, Long itemId, IdempotentEnum idempotentEnum, String businessId) {//todo 分布式锁
|
||||
String idempotent = getIdempotent(itemId, idempotentEnum, businessId);
|
||||
UserBackpack userBackpack = userBackpackDao.getByIdp(idempotent);
|
||||
//幂等检查
|
||||
if (Objects.nonNull(userBackpack)) {
|
||||
return;
|
||||
}
|
||||
//业务检查
|
||||
ItemConfig itemConfig = itemCache.getById(itemId);
|
||||
if (ItemTypeEnum.BADGE.getType().equals(itemConfig.getType())) {//徽章类型做唯一性检查
|
||||
Integer countByValidItemId = userBackpackDao.getCountByValidItemId(uid, itemId);
|
||||
if (countByValidItemId > 0) {//已经有徽章了不发
|
||||
return;
|
||||
}
|
||||
}
|
||||
//发物品
|
||||
UserBackpack insert = UserBackpack.builder()
|
||||
.itemId(itemId)
|
||||
.status(YesOrNoEnum.NO.getStatus())
|
||||
.idempotent(idempotent)
|
||||
.build();
|
||||
userBackpackDao.save(insert);
|
||||
}
|
||||
|
||||
private String getIdempotent(Long itemId, IdempotentEnum idempotentEnum, String businessId) {
|
||||
return String.format("%d_%d_%s", itemId, idempotentEnum.getType(), businessId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user