Jelajahi Sumber

实体类提出共通 提交

zjy 1 bulan lalu
induk
melakukan
438f12f44f

+ 246 - 0
alien-gateway/src/main/java/shop/alien/gateway/config/BaseRedisService.java

@@ -0,0 +1,246 @@
+package shop.alien.gateway.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2022/3/14 15:14
+ */
+@Component
+public class BaseRedisService {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 添加List值, 向右
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setListRight(String key, String value) {
+        stringRedisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 添加List, 所有
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setList(String key, List<String> value) {
+        stringRedisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 从List左边取值并删除
+     *
+     * @param key 键
+     * @return 删除的值
+     */
+    public String getListLeft(String key) {
+        return stringRedisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取List所有
+     *
+     * @param key 键
+     * @return List<String>
+     */
+    public List<String> getList(String key) {
+        return stringRedisTemplate.opsForList().range(key, 0, -1);
+    }
+
+
+    /**
+     * 添加Set
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setSetList(String key, String value) {
+        stringRedisTemplate.opsForSet().add(key, value);
+    }
+
+    /**
+     * 获取Set
+     *
+     * @param key 键
+     * @return Set<String>
+     */
+    public Set<String> getSetList(String key) {
+        return stringRedisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 添加String值, 不设置过期时间
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setString(String key, String value) {
+        set(key, value, null);
+    }
+
+    /**
+     * 添加String值, 并设置过期时间
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    public void setString(String key, String value, Long timeOut) {
+        set(key, value, timeOut);
+    }
+
+    /**
+     * 设置超时时间
+     *
+     * @param key     键
+     * @param timeOut 时长(秒)
+     */
+    public void setTimeOut(String key, Long timeOut) {
+        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 传入object对象
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    private void set(String key, Object value, Long timeOut) {
+        if (value != null) {
+            if (value instanceof String) {
+                String setValue = (String) value;
+                stringRedisTemplate.opsForValue().set(key, setValue);
+            }
+            //设置有效期
+            if (timeOut != null) {
+                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+            }
+        }
+    }
+
+    /**
+     * 使用key查找redis信息
+     *
+     * @param key 键
+     * @return
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 使用key删除redis信息
+     *
+     * @param key 键
+     */
+    public void delete(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+
+    /**
+     * 添加地理信息
+     *
+     * @param point
+     * @param content
+     * @param type
+     * @return
+     */
+    public Long inGeolocation(Point point, String content, String type) {
+        return stringRedisTemplate.opsForGeo().add(type, point, content);
+    }
+
+
+    /**
+     * 计算两个位置距离
+     *
+     * @param content
+     * @param contentII
+     * @param type
+     * @return
+     */
+    public Distance computeDistance(String content, String contentII, String type) {
+        return stringRedisTemplate.opsForGeo().distance(type, content, contentII, Metrics.KILOMETERS);
+
+    }
+
+    /**
+     * 获取成员经纬度
+     *
+     * @param contentArray
+     * @return
+     */
+    public List<Point> positions(String type, String... contentArray) {
+        return stringRedisTemplate.opsForGeo().position(type, contentArray);
+    }
+
+
+    /**
+     * 根据content查询附近商家
+     *
+     * @param content
+     * @param distance 搜索半径(单位:千米)
+     * @param count    获取几条
+     * @param type     name or 主键
+     * @return
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(String content, double distance, long count, String type) {
+
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        content,
+                        new Distance(distance, Metrics.KILOMETERS),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()   // 包含距离信息
+                                .includeCoordinates()// 包含坐标信息
+                                .sortAscending()     // 按距离升序
+                                .limit(count));   // 限制返回数量
+    }
+
+    /**
+     * 根据坐标查询附近商家
+     *
+     * @param point    经度 纬度
+     * @param distance 搜索半径(单位:千米)
+     * @param count    最大返回数量
+     * @return 附近商家地理信息结果集
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(Point point, double distance, long count, String type) {
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        new Circle(point, new Distance(distance, Metrics.KILOMETERS)),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()
+                                .includeCoordinates()
+                                .sortAscending()
+                                .limit(count));
+    }
+
+
+    /**
+     * 删除GEO类型中的成员
+     *
+     * @param type    GEO数据对应的键名(对应你代码中的type参数)
+     * @param members 要删除的成员名称
+     * @return 实际删除的成员数量
+     */
+    public Long removeGeoMember(String type, String... members) {
+        return stringRedisTemplate.opsForZSet().remove(type, (Object[]) members);
+    }
+
+
+}

+ 87 - 0
alien-gateway/src/main/java/shop/alien/gateway/config/JWTFilterConfig.java

@@ -0,0 +1,87 @@
+package shop.alien.gateway.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Token 拦截器配置
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/25 16:59
+ */
+@Configuration
+public class JWTFilterConfig implements WebMvcConfigurer {
+
+    @Autowired
+    private JWTInterceptor jwtInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(jwtInterceptor)
+                .addPathPatterns("/**")
+                //login
+                .excludePathPatterns("/user/userLogin")
+                //门店用户登录
+                .excludePathPatterns("/store/user/login")
+                // 门店用户登录校验
+                .excludePathPatterns("/store/user/checkLogin")
+                // 门店用户注册
+                .excludePathPatterns("/store/user/register")
+                // 门店用户注册校验
+                .excludePathPatterns("/store/user/checkRegister")
+                //文件上传
+                .excludePathPatterns("/file/upload")
+                //阿里回调
+                .excludePathPatterns("/ali/notify")
+                //发送短信
+                .excludePathPatterns("/ali/sendSms")
+                //修改密码
+                .excludePathPatterns("/store/user/updatePassword")
+                //根据手机号获取用户
+                .excludePathPatterns("/store/user/getUserByPhone")
+                //社区列表
+                .excludePathPatterns("/userDynamics/getUserDynamics")
+                //活动列表
+                .excludePathPatterns("/activity/getActivityList")
+                //联名卡列表
+                .excludePathPatterns("/userbrandedcard/getBrandedCardList")
+                //查询字典
+                .excludePathPatterns("/dicts/getLifeDictByTypeName")
+                //首页轮播图
+                .excludePathPatterns("/userCarouselImage/getUserCarouselImage")
+                //查询商铺列表
+                .excludePathPatterns("/userstore/getStoreList")
+                //websocket
+                .excludePathPatterns("/socket/**")
+                .excludePathPatterns("/websocket/**")
+                //druid
+                .excludePathPatterns("/druid/**")
+                //swagger
+                .excludePathPatterns("/webjars/**")
+                .excludePathPatterns("/swagger-resources/**")
+                .excludePathPatterns("/v2/**")
+                .excludePathPatterns("/jrebel/statistics")
+                .excludePathPatterns("/error")
+                .excludePathPatterns("/doc.html")
+                //web数据中台登录接口
+                .excludePathPatterns("/sys/login")
+                .excludePathPatterns("/img/getCarouselImage")
+                //八大类相关接口
+                .excludePathPatterns("/essential-module-information/getChildByParentId")
+                .excludePathPatterns("/essential-module-information/getEssentialModuleInformationList")
+                .excludePathPatterns("/essential-module-information/getList")
+                .excludePathPatterns("/essential-module-information/getPrimaryData")
+                .excludePathPatterns("/version/getLatestVersion")
+                .excludePathPatterns("/elasticSearch/*")
+//                .excludePathPatterns("/**");
+                .excludePathPatterns("/user-violation/level")
+                .excludePathPatterns("/store/user/updatePassword")
+                .excludePathPatterns("/testInfo/getTestInfo")
+                .excludePathPatterns("/file/uploadApp")
+        ;
+    }
+
+}

+ 120 - 0
alien-gateway/src/main/java/shop/alien/gateway/config/JWTInterceptor.java

@@ -0,0 +1,120 @@
+package shop.alien.gateway.config;
+
+import com.auth0.jwt.exceptions.AlgorithmMismatchException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.CorsUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.store.mapper.StoreUserMapper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Token校验
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/25 16:59
+ */
+@Slf4j
+@Component
+public class JWTInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private BaseRedisService baseRedisService;
+
+    @Autowired
+    private StoreUserMapper storeUserMapper;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        if (CorsUtils.isPreFlightRequest(request)) {
+            // 这是一个OPTIONS请求,我们可以选择放行
+            return true;
+        }
+        //获取请求头中的token
+        String token = request.getHeader("Authorization");
+        log.info("====================>token值: " + token);
+        String path = request.getRequestURI();
+        log.info("====================>path: " + path);
+        Map<String, Object> map = new HashMap<>();
+        int errorType = 0;
+        try {
+            JWTUtils.TokenVerify(token);
+            DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
+            log.info("phone:{}", tokenInfo.getClaim("phone").asString());
+            String phone = tokenInfo.getClaim("phone").asString();
+            log.info("userType:{}", tokenInfo.getClaim("userType").asString());
+            String deviceType = tokenInfo.getClaim("userType").asString();
+            String redisKey;
+            //区分
+            if ("web".equals(deviceType)) {
+                //管理端单设备登录
+//                redisKey = deviceType + "_" + tokenInfo.getClaim("userName").asString();
+                //不限制
+                return true;
+            } else {
+                redisKey = deviceType + "_" + tokenInfo.getClaim("phone").asString();
+            }
+            String redisVal = baseRedisService.getString(redisKey);
+            if (StringUtils.isEmpty(redisVal) || !token.equals(redisVal)) {
+                //判断程序是否为用户禁用
+                StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getPhone, phone));
+                if (storeUser.getStatus() == 1) {
+                    map.put("msg", "你的账号已被禁用");
+                    //别问, 问就是约定俗成
+                    map.put("code", 777);
+                } else {
+                    map.put("msg", "用户在别处登录");
+                    //别问, 问就是约定俗成
+                    map.put("code", 666);
+                }
+
+                map.put("success", false);
+                String json = new ObjectMapper().writeValueAsString(map);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().print(json);
+                return false;
+            }
+            //放行请求
+            return true;
+        } catch (SignatureVerificationException e) {
+            errorType = 1;
+            log.error("JWTInterceptor SignatureVerificationException Msg={}", e.getMessage());
+            map.put("msg", "无效签名");
+        } catch (TokenExpiredException e) {
+            errorType = 2;
+            log.error("JWTInterceptor TokenExpiredException Msg={}", e.getMessage());
+            map.put("msg", "token已过期");
+        } catch (AlgorithmMismatchException e) {
+            errorType = 3;
+            log.error("JWTInterceptor AlgorithmMismatchException Msg={}", e.getMessage());
+            map.put("msg", "算法不一致");
+        } catch (Exception e) {
+            errorType = 4;
+            log.error("JWTInterceptor Exception Msg={}", e.getMessage());
+            map.put("msg", "token无效");
+        }
+        log.info("====================>token无效类型: " + errorType);
+        map.put("code", 401);
+        map.put("success", false);
+        //使用jackson将map转为json
+        String json = new ObjectMapper().writeValueAsString(map);
+        response.setContentType("application/json;charset=UTF-8");
+        response.getWriter().print(json);
+        return false;
+    }
+
+}

+ 66 - 0
alien-gateway/src/main/java/shop/alien/gateway/config/JWTUtils.java

@@ -0,0 +1,66 @@
+package shop.alien.gateway.config;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTCreator;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Token工具类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/25 16:58
+ */
+public class JWTUtils {
+
+    private static final String SIGN = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+    //1000ms x 60秒 x 60分钟 x 24小时 x 30天
+    private static final Long EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30L;
+
+    /**
+     * 生成Token
+     */
+    public static String createToken(Map<String, String> map) {
+        //创建JWTBuilder
+        JWTCreator.Builder builder = JWT.create();
+        //设置payload
+        map.forEach(builder::withClaim);
+        //设置过期时间和签名,生成token
+        return builder.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).sign(Algorithm.HMAC256(SIGN));
+    }
+
+    /**
+     * 验证token
+     */
+    public static void TokenVerify(String token) {
+        JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
+    }
+
+    /**
+     * 获取token信息
+     */
+    public static DecodedJWT getTokenInfo(String token) {
+        return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
+    }
+
+    /**
+     * 非控制器获取token
+     */
+    public static String getToken() {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (attributes != null) {
+            HttpServletRequest request = attributes.getRequest();
+            String token = request.getHeader("Authorization");
+            return token;
+        }
+        return null;
+    }
+}

+ 188 - 0
alien-gateway/src/main/java/shop/alien/gateway/controller/LifeUserController.java

@@ -0,0 +1,188 @@
+package shop.alien.gateway.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeFans;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.gateway.entity.vo.LifeUserVo;
+import shop.alien.gateway.mapper.LifeUserMapper;
+import shop.alien.gateway.service.LifeUserService;
+import shop.alien.gateway.util.FileUpload;
+import shop.alien.gateway.util.RandomCreateUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 用户
+ */
+@Api(tags = {"一期-用户"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/user")
+@RequiredArgsConstructor
+public class LifeUserController {
+
+    private final LifeUserService service;
+
+    private final FileUpload fileUpload;
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final RandomCreateUtil randomCreateUtil;
+
+    @ApiOperation("用户登录")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({@ApiImplicitParam(name = "phoneNum", value = "手机号", dataType = "String", paramType = "query", required = true)})
+    @GetMapping("/userLogin")
+    public R<LifeUserVo> userLogin(@RequestParam("phoneNum") String phoneNum) {
+        log.info("LifeUserController.userLogin?phoneNum={}", phoneNum);
+        LifeUserVo userVo = service.userLogin(phoneNum);
+        if (null == userVo) {
+            return R.fail("登录失败");
+        }
+        return R.data(userVo);
+    }
+    @ApiOperation("查询用户信息")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键id", dataType = "String", paramType = "query", required = true)})
+    @GetMapping("/selectUserById")
+    public R<LifeUser> selectUserById(@RequestParam("id") String id) {
+        log.info("LifeUserController.selectUserById?id={}", id);
+        LifeUser lifeUserVo = new LifeUser();
+        LifeUser user = lifeUserMapper.selectById(id);
+        if (null == user) {
+            return R.fail("查询失败");
+        }
+//        BeanUtils.copyProperties(user, lifeUserVo);
+//        // 添加 是否拉黑标识,是否是达人标识
+//        //达人标识 0 不是达人 1 是达人
+//        LambdaUpdateWrapper<LifeUserExpert> wrapper = new LambdaUpdateWrapper<>();
+//        wrapper.eq(LifeUserExpert::getUserId, user.getId());
+//        LifeUserExpert lifeUserExpert = lifeUserExpertMapper.selectOne(wrapper);
+//        if (null != lifeUserExpert) {
+//            lifeUserVo.setIsExpert("1");
+//        } else {
+//            lifeUserVo.setIsExpert("0");
+//        }
+        // 被拉黑标识 0 未拉黑 1 已拉黑(待定)
+
+        return R.data(user);
+    }
+
+    @ApiOperation("修改用户信息")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/modifyUser")
+    public R<Boolean> modifyUser(@RequestBody LifeUser user) {
+        log.info("LifeUserController.modifyUser?user={}", user.toString());
+        int num = service.modifyUser(user);
+        if (num == 0) {
+            return R.fail("修改失败");
+        }
+        return R.success("修改成功");
+    }
+
+    @ApiOperation("关注")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/addFans")
+    public R<Boolean> addFans(@RequestBody LifeFans fans) {
+        log.info("LifeUserController.addFans?fans={}", fans.toString());
+        int num = service.addFans(fans);
+        if (num == 0) {
+            return R.fail("关注失败");
+        }
+        return R.success("关注成功");
+    }
+
+    @ApiOperation("取消关注")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/cancelFollewed")
+    public R<Boolean> cancelFollewed(@RequestBody LifeFans fans) {
+        log.info("LifeUserController.cancelFollewed?fans={}", fans.toString());
+        int num = service.cancelFans(fans);
+        if (num == 0) {
+            return R.fail("取消失败");
+        }
+        return R.success("取消成功");
+    }
+
+    @ApiOperation("搜索商家或用户")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/getStoreAndUserByName")
+    public R<IPage<LifeUserVo>> getStoreAndUserByName(@RequestBody LifeUserVo vo) {
+        log.info("LifeUserController.getStoreAndUserByName?vo={}", vo.toString());
+        return R.data(service.getStoreAndUserByName(vo));
+    }
+
+    @GetMapping("/getUserById")
+    public LifeUser getUserById(@RequestParam("id") String id) {
+        log.info("LifeUserController.getUserById?id={}", id);
+        return service.getUserById(id);
+    }
+
+    @PostMapping("/addUser")
+    public int addOrUpdateUser(LifeUser user, @RequestParam(value = "image", required = false) MultipartFile image) throws Exception {
+        log.info("LifeUserController.addOrUpdateUser?user={}&image={}", user.toString(), image.getOriginalFilename());
+        String path = "";
+        if (!StringUtils.isEmpty(image.getOriginalFilename())) {
+            path = fileUpload.saveImage(image);
+        }
+        // 设置图片路径
+        user.setUserImage(path);
+        //邀请码
+
+        user.setInvitedNum(randomCreateUtil.getRandomNStr(20));
+        return service.addOrUpdateUser(user);
+    }
+
+    @GetMapping("/getUserPage")
+    public Map<String, Object> getStoresPage(@RequestParam(defaultValue = "1") int page,
+                                             @RequestParam(defaultValue = "10") int size,
+                                             @RequestParam(required = false) String realName,
+                                             @RequestParam(required = false) String userPhone) {
+        log.info("LifeUserController.getStoresPage?page={},size={},realName={},userPhone={}", page, size, realName, userPhone);
+        IPage<LifeUser> stores = service.getStoresPage(page, size, realName, userPhone);
+        Map<String, Object> response = new HashMap<>();
+        response.put("data", stores.getRecords());
+        response.put("total", stores.getRecords().size());
+        response.put("current", stores.getCurrent());
+        response.put("size", stores.getSize());
+        return response;
+    }
+
+    /**
+     * 用户端注销用户
+     */
+    @ApiOperation("用户端注销用户")
+    @PostMapping("/liftCancelAccount")
+    public R<Boolean> liftCancelAccount(@RequestBody LifeUser user) {
+        log.info("StoreUserController.liftCancelAccount?LifeUser={}", user);
+        service.liftCancelAccount(user);
+        return R.success("注销成功");
+    }
+
+    /**
+     * 用户端撤回注销用户
+     */
+    @ApiOperation("用户端撤回注销用户")
+    @PostMapping("/liftCancelAccountUn")
+    public R<Boolean> liftCancelAccountUn(@RequestBody LifeUser user) {
+        log.info("StoreUserController.liftCancelAccountUn?LifeUser={}", user);
+        service.liftCancelAccountUn(user.getId());
+        return R.success("撤回注销成功");
+    }
+
+    @GetMapping("/getUserByPhone")
+    public R<LifeUser> getUserByPhone(@RequestParam("phone") String phone) {
+        log.info("LifeUserController.getUserByPhone?phone={}", phone);
+        return R.data(service.getUserByPhone(phone));
+    }
+
+}

+ 48 - 0
alien-gateway/src/main/java/shop/alien/gateway/entity/vo/LifeMessageVo.java

@@ -0,0 +1,48 @@
+package shop.alien.gateway.entity.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import shop.alien.entity.store.LifeMessage;
+
+@Data
+@JsonInclude
+@NoArgsConstructor
+@ApiModel(value = "LifeMessageVo对象", description = "消息通知")
+public class LifeMessageVo extends LifeMessage {
+
+    @ApiModelProperty(value = "名称")
+    private String userName;
+
+    @ApiModelProperty(value = "头像")
+    private String userImage;
+
+    @ApiModelProperty(value = "商户头像")
+    private String storeImg;
+
+    @ApiModelProperty(value = "唯一标识")
+    private String phoneId;
+//
+//    @ApiModelProperty(value = "消息列表")
+//    private List<LifeMessage> messageList;
+
+    @ApiModelProperty(value = "未读消息数量")
+    private Long notReadCount;
+
+    @ApiModelProperty(value = "我是否关注对方 0-未关注 1-已关注")
+    private String isFollowThis;
+
+    @ApiModelProperty(value = "对方是否关注我 0-未关注 1-已关注")
+    private String isFollowMe;
+
+    @ApiModelProperty(value = "是否是商户 0-不是商户 1-是商户")
+    private String isMerchant;
+
+    @ApiModelProperty(value = "接收方图片展示")
+    private String receiverImg;
+
+    @ApiModelProperty(value = "发送方图片展示")
+    private String senderImg;
+}

+ 62 - 0
alien-gateway/src/main/java/shop/alien/gateway/entity/vo/LifeUserVo.java

@@ -0,0 +1,62 @@
+package shop.alien.gateway.entity.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.LifeUser;
+
+@Data
+@JsonInclude
+public class LifeUserVo extends LifeUser {
+
+    private String isGuanzhu;
+
+    private String token;
+
+    private String badNum;
+
+    private Integer fansCount;
+
+    private Integer likesNumber;
+
+    private Integer expertId;
+
+    private Integer expertCode;
+
+    private Integer consumeNum;
+
+    private double consumePrice;
+
+    @ApiModelProperty(value = "前端查询集合Label")
+    private String Label;
+
+    @ApiModelProperty(value = "前端查询集合Value")
+    private Integer value;
+
+    @ApiModelProperty(value = "分页页数")
+    private int page;
+
+    @ApiModelProperty(value = "分页条数")
+    private int size;
+
+    @ApiModelProperty(value = "用户id")
+    private String userId;
+
+    @ApiModelProperty(value = "搜索名称")
+    private String searchName;
+
+    @ApiModelProperty(value = "唯一标识")
+    private String phoneId;
+
+    @ApiModelProperty(value = "我是否关注对方 0-未关注 1-已关注")
+    private String isFollowThis;
+
+    @ApiModelProperty(value = "商家或用户名称")
+    private String storeUserName;
+
+    @ApiModelProperty(value = "头像图片")
+    private String imgUrl;
+
+    @ApiModelProperty(value = "简介")
+    private String blurb;
+}

+ 205 - 0
alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeFansMapper.java

@@ -0,0 +1,205 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.LifeFans;
+import shop.alien.store.entity.vo.LifeFansVo;
+
+import java.util.List;
+
+@Mapper
+public interface LifeFansMapper extends BaseMapper<LifeFans> {
+
+    @Select("select foll.*,lb.id blackListid, if(isnull(fans.id), 0, 1) isFollowMe, 1 as isFollowThis,  " +
+            "  (select count(1) from life_fans where fans_id= foll.phoneId and delete_flag =0) followNum, " +
+            "  (select count(1) from life_fans where followed_id= foll.phoneId and delete_flag =0) fansNum from ( " +
+            "    with follow as (   " +
+            "        select substring_index(followed_id, '_', 1) as flag, substring_index(followed_id, '_', -1) as phone   " +
+            "        from life_fans   " +
+            "        where delete_flag = 0 and fans_id = #{fansId} " +
+            "    )   " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, info.store_blurb blurb, 1 blockedType,user.id blockedId" +
+            "    from follow foll " +
+            "    join store_user user on foll.phone = user.phone " +
+            "    join store_info info on info.id = user.store_id " +
+            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    union " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId" +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone   " +
+            "    where foll.flag = 'user' and user.delete_flag = 0   " +
+            ") foll   " +
+            "left join life_fans fans on fans.fans_id = foll.phoneId and fans.followed_id = #{fansId} and fans.delete_flag = 0 " +
+            "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0" +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMyFollowed(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+
+    @Select("select foll.*, if(isnull(fans.id), 0, 1) isFollowMe, 1 as isFollowThis from ( " +
+            "    with follow as (   " +
+            "        select substring_index(followed_id, '_', 1) as flag, substring_index(followed_id, '_', -1) as phone   " +
+            "        from life_fans   " +
+            "        where delete_flag = 0 and fans_id = #{fansId} " +
+            "    )   " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId " +
+            "    from follow foll " +
+            "    join store_user user on foll.phone = user.phone " +
+            "    join store_info info on info.id = user.store_id " +
+            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    union " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId " +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone   " +
+            "    where foll.flag = 'user' and user.delete_flag = 0   " +
+            ") foll " +
+            "left join life_fans fans on fans.fans_id = foll.phoneId and fans.followed_id = #{fansId} and fans.delete_flag = 0 ")
+    List<LifeFansVo> getMyFollowedAll(@Param("fansId") String fansId);
+
+    @Select("select foll.*,lb.id blackListid, if(isnull(fans.id), 0, 1) isFollowThis, 1 as isFollowMe, " +
+            "    (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
+            "    (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "from ( " +
+            "    with follow as ( " +
+            "        select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone " +
+            "        from life_fans " +
+            "        where delete_flag = 0 and followed_id = #{fansId} " +
+            "    ) " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, info.store_blurb blurb, 1 blockedType,user.id blockedId" +
+            "    from follow foll " +
+            "    join store_user user on foll.phone = user.phone " +
+            "    join store_info info on info.id = user.store_id " +
+            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    union " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId" +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone " +
+            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            ") foll " +
+            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{fansId} and fans.delete_flag = 0 " +
+            "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0" +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMyFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+    @Select("select foll.*, if(isnull(fans.id), 0, 1) isFollowThis, 1 as isFollowMe, count(fans2.id) fansNum, count(fans3.id) followNum from ( " +
+            "    with follow as ( " +
+            "    select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone " +
+            "    from life_fans " +
+            "    where delete_flag = 0 and followed_id = #{fansId} " +
+            "    ) " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, info.store_blurb blurb " +
+            "    from follow foll " +
+            "    join store_user user on foll.phone = user.phone " +
+            "    join store_info info on info.id = user.store_id " +
+            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            ") foll " +
+            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{fansId} and fans.delete_flag = 0 " +
+            "left join life_fans fans2 on fans2.followed_id = foll.phoneId and fans2.delete_flag = 0 " +
+            "left join life_fans fans3 on fans3.fans_id = foll.phoneId and fans3.delete_flag = 0 " +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMyStoreFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+    @Select("select foll.*, if(isnull(fans.id), 0, 1) isFollowThis, 1 as isFollowMe, " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            " from ( " +
+            "    with follow as ( " +
+            "    select substring_index(fans_id, '_', 1) as flag, substring_index(fans_id, '_', -1) as phone " +
+            "    from life_fans " +
+            "    where delete_flag = 0 and followed_id = #{fansId} " +
+            "    ) " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb " +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone " +
+            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            ") foll " +
+            "left join life_fans fans on fans.followed_id = foll.phoneId and fans.fans_id = #{fansId} and fans.delete_flag = 0 " +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMyUserFans(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+    @Select("select foll.*, lb.id blackListid, 1 as isFollowThis, 1 as isFollowMe, " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "from ( " +
+            "    with follow as ( " +
+            "        select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone " +
+            "        from life_fans fans1 " +
+            "        join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
+            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{fansId} " +
+            "    ) " +
+            "    select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId, info.store_blurb blurb, 1 blockedType,user.id blockedId " +
+            "    from follow foll " +
+            "    join store_user user on foll.phone = user.phone " +
+            "    join store_info info on info.id = user.store_id " +
+            "    left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "    union " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb, 2 blockedType,user.id blockedId " +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone " +
+            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            ") foll " +
+            "left join life_blacklist lb on lb.blocked_type = foll.blockedType and lb.blocked_id = foll.blockedId and lb.blocker_type = #{blockerType} and lb.blocker_id = #{blockerId} and lb.delete_flag = 0" +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMutualAttention(IPage<LifeFansVo> iPage, @Param("fansId") String fansId,  @Param("blockerType") String blockerType, @Param("blockerId") String blockerId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+    @Select("select foll.*, 1 as isFollowThis, 1 as isFollowMe, " +
+            "       (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
+            "       (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "from ( " +
+            "    with follow as ( " +
+            "        select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone " +
+            "        from life_fans fans1 " +
+            "        join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
+            "        where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{fansId} " +
+            "    ) " +
+            "    select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId, user.jianjie blurb " +
+            "    from follow foll " +
+            "    join life_user user on foll.phone = user.user_phone " +
+            "    where foll.flag = 'user' and user.delete_flag = 0 " +
+            ") foll " +
+            "${ew.customSqlSegment} ")
+    IPage<LifeFansVo> getMutualAttentionUser(IPage<LifeFansVo> iPage, @Param("fansId") String fansId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> wrapper);
+
+    @Select("select (select count(1) from life_fans where fans_id = #{phoneId} and delete_flag = 0 and followed_id not REGEXP '_$') followNum, " +
+            "(select count(1) from life_fans where followed_id= #{phoneId} and delete_flag = 0 and fans_id not REGEXP '_$') fansNum, " +
+            "(select count(1) from ( " +
+            "        select foll.*, 1 as isFollowThis, 1 as isFollowMe, " +
+            "               (select count(1) from life_fans fans2 where fans2.followed_id = foll.phoneId and fans2.delete_flag = 0) fansNum, " +
+            "               (select count(1) from life_fans fans3 where fans3.fans_id = foll.phoneId and fans3.delete_flag = 0) followNum " +
+            "        from ( " +
+            "            with follow as ( " +
+            "                select substring_index(fans1.followed_id, '_', 1) as flag, substring_index(fans1.followed_id, '_', -1) as phone " +
+            "                from life_fans fans1 " +
+            "                join life_fans fans2 on fans1.followed_id = fans2.fans_id and fans1.fans_id = fans2.followed_id " +
+            "                where fans1.delete_flag = 0 and fans2.delete_flag = 0 and fans1.fans_id = #{phoneId} " +
+            "            ) " +
+            "            select info.id, info.store_name name, img.img_url image, concat('store_', user.phone) phoneId " +
+            "            from follow foll " +
+            "            join store_user user on foll.phone = user.phone " +
+            "            join store_info info on info.id = user.store_id " +
+            "            left join store_img img on img.store_id = user.store_id and img.img_type = '10' and img.delete_flag = 0 " +
+            "            where foll.flag = 'store' and user.delete_flag = 0 and info.delete_flag = 0 " +
+            "            union " +
+            "            select user.id, user.user_name name, user.user_image image, concat('user_', user.user_phone) phoneId " +
+            "            from follow foll " +
+            "            join life_user user on foll.phone = user.user_phone " +
+            "            where foll.flag = 'user' and user.delete_flag = 0 " +
+            "        ) foll " +
+            "    ) a " +
+            ") friendNum, " +
+            "( " +
+            "    select count(id) " +
+            "    from life_user_dynamics " +
+            "    where delete_flag = 0 and phone_id = #{phoneId} " +
+            ") dynamicsNum")
+    LifeFansVo getHomePageInfo(@Param("phoneId") String phoneId);
+}

+ 75 - 0
alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeMessageMapper.java

@@ -0,0 +1,75 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.store.entity.vo.LifeFansVo;
+import shop.alien.store.entity.vo.LifeMessageVo;
+
+import java.util.List;
+
+/**
+ * 消息
+ */
+@Mapper
+public interface LifeMessageMapper extends BaseMapper<LifeMessage> {
+
+    @Select("select id, user_name userName, user_image userImage, concat('user_', user_phone) phoneId " +
+            "from life_user " +
+            "where delete_flag = 0 and user_phone != '' and user_phone in (${userPhones})  " +
+            "union " +
+            "select info.id, info.store_name userName, img.img_url userImage, concat('store_', user.phone) phoneId " +
+            "from store_user user " +
+            "join store_info info on info.id = user.store_id " +
+            "left join store_img img on img.store_id = info.id and img.img_type = '10' and img.delete_flag = 0 " +
+            "where user.delete_flag = 0 and info.delete_flag = 0 and user.phone != '' and phone in (${storePhones}) ")
+    List<LifeMessageVo> getLifeUserAndStoreUserByPhone(@Param("storePhones") String storePhones, @Param("userPhones") String userPhones);
+
+    @Select("with message_num as ( " +
+            "    with message as ( " +
+            "        select id, type, content, created_time, if(sender_id = #{phoneId}, receiver_id, sender_id) phoneId, sender_id, receiver_id, is_read " +
+            "        from life_message " +
+            "        where delete_flag = 0 and (sender_id = #{phoneId} or receiver_id = #{phoneId}) " +
+            "               and (instr(delete_phone_id, #{phoneId}) is null or instr(delete_phone_id, #{phoneId}) = 0)" +
+            "    ) " +
+            "    select *, row_number() over (partition by phoneId order by created_time desc ) num " +
+            "    from message " +
+            ") " +
+            "select id, type, phoneId, content, created_time createdTime, is_read " +
+            "from message_num " +
+            "${ew.customSqlSegment}")
+    IPage<LifeMessageVo> getLifeMessagePageByPhoneId(IPage<LifeMessageVo> iPage, @Param("phoneId") String phoneId, @Param(Constants.WRAPPER) QueryWrapper<LifeFansVo> dynamicsWrapper);
+
+    @Update("update life_message set delete_phone_id = (if(delete_phone_id is null, #{receiverId}, concat(delete_phone_id, ',', #{receiverId}))) ,delete_flag = '1'" +
+            "where (receiver_id = #{senderId} and sender_id = #{receiverId}) or (sender_id = #{senderId} and receiver_id = #{receiverId}) ")
+    int deleteMessageByPhoneId(@Param("senderId") String senderId, @Param("receiverId") String receiverId);
+
+//    @Select("select lm.id,lm.updated_time,lm.created_user_id,lm.receiver_name,lm.is_read,lm.type,lm.content," +
+//            "lm.current_time,lm.delete_flag,lm.sender_id,lm.sender_name,lm.receiver_id,lm.delete_phone_id," +
+//            "lm.created_time,lm.updated_user_id,lu.user_image,img.img_url storeImg" +
+//            "    from life_message lm" +
+//            "    left join life_user lu " +
+//            "    on SUBSTRING(lm.sender_id, INSTR(lm.sender_id, '_') + 1) = lu.user_phone" +
+//            "        left join store_user su on SUBSTRING(lm.sender_id, INSTR(lm.sender_id, '_') + 1) = su.phone " +
+//            "        left join store_info s on su.store_id = s.id" +
+//            "        left join store_img img on img.store_id = s.id and img.img_type = '10' and img.delete_flag = 0 "+
+//            "    where lm.delete_flag = 0 and su.delete_flag = 0 and s.delete_flag = 0" +
+//            "    and ( ((lm.receiver_id = #{senderId} and lm.sender_id = #{receiverId})" +
+//            "    or (lm.sender_id = #{senderId} and lm.receiver_id = #{receiverId})) )" +
+//            "    order by lm.created_time asc")
+@Select("select lm.id,lm.updated_time,lm.created_user_id,lm.receiver_name,lm.is_read,lm.type,lm.content,lm.current_time,lm.delete_flag,lm.sender_id,lm.sender_name,lm.receiver_id,lm.delete_phone_id,lm.created_time,lm.updated_user_id,lu.user_image" +
+        "    from life_message lm" +
+        "    left join life_user lu " +
+        "    on SUBSTRING(lm.sender_id, INSTR(lm.sender_id, '_') + 1) = lu.user_phone"+
+        "    where lm.delete_flag = 0" +
+        "    and ( ((lm.receiver_id = #{senderId} and lm.sender_id = #{receiverId})" +
+        "    or (lm.sender_id = #{senderId} and lm.receiver_id = #{receiverId})) )" +
+        "    order by lm.created_time asc")
+    List<LifeMessageVo> selectUserImageLists(@Param("receiverId") String receiverId,@Param("senderId") String senderId);
+}

+ 10 - 0
alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeNoticeMapper.java

@@ -0,0 +1,10 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.LifeNotice;
+
+@Mapper
+public interface LifeNoticeMapper extends BaseMapper<LifeNotice> {
+
+}

+ 58 - 0
alien-gateway/src/main/java/shop/alien/gateway/mapper/LifeUserMapper.java

@@ -0,0 +1,58 @@
+package shop.alien.gateway.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.store.entity.vo.LifeFansVo;
+import shop.alien.store.entity.vo.LifeUserOrderVo;
+import shop.alien.store.entity.vo.LifeUserVo;
+
+import java.util.List;
+
+/**
+ * 用户
+ */
+@Mapper
+public interface LifeUserMapper extends BaseMapper<LifeUser> {
+
+    @Select("select info.store_name, user.phone, img.img_url " +
+            "from store_info info " +
+            "join store_user user on user.store_id = info.id " +
+            "left join store_img img on img.store_id = info.id and img.img_type = '10' and img.delete_flag = 0 " +
+            "where info.delete_flag = 0 and user.delete_flag = 0 " +
+            "and substring_index(#{phoneId}, '_', 1) = 'store' " +
+            "and substring_index(#{phoneId}, '_', -1) = user.phone " +
+            "union " +
+            "select user_name name, user_phone phone, user_image img_url " +
+            "from life_user " +
+            "where delete_flag = 0 and substring_index(#{phoneId}, '_', 1) = 'user' and substring_index(#{phoneId}, '_', -1) = user_phone")
+    LifeFansVo getUserInfoByPhoneId(@Param("phoneId") String phoneId);
+
+
+    LifeFansVo getUserInfoByPhoneIdList(@Param("phoneId") String phoneId);
+
+    List<LifeUserOrderVo> getUserOrderDetail(Integer userId);
+
+    @Select("select * from ( " +
+            "    select store.id, concat('store_', user.phone) phoneId, img.img_url imgUrl, store_blurb blurb, store.store_name storeUserName " +
+            "    from store_info store " +
+            "    join store_user user on user.store_id = store.id " +
+            "    left join store_img img on img.store_id = store.id and img.img_type = '10' and img.delete_flag = 0 " +
+            "    where store.delete_flag = 0 " +
+            "    and user.delete_flag = 0 " +
+            "    union all " +
+            "    select id, concat('user_', user_phone) phoneId, user_image imgUrl, jianjie blurb, user_name name " +
+            "    from life_user " +
+            "    where delete_flag = 0 " +
+            ") a " +
+            "${ew.customSqlSegment}")
+    IPage<LifeUserVo> getStoreAndUserByName(IPage<LifeUserVo> iPage, @Param(Constants.WRAPPER) QueryWrapper<LifeUserVo> wrapper);
+
+    LifeUserVo getRemoveUser(@Param("id") String id);
+
+}

+ 211 - 0
alien-gateway/src/main/java/shop/alien/gateway/service/LifeUserService.java

@@ -0,0 +1,211 @@
+package shop.alien.gateway.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Triple;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.store.LifeFans;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.gateway.config.BaseRedisService;
+import shop.alien.gateway.config.JWTUtils;
+import shop.alien.gateway.entity.vo.LifeMessageVo;
+import shop.alien.gateway.entity.vo.LifeUserVo;
+import shop.alien.gateway.mapper.LifeFansMapper;
+import shop.alien.gateway.mapper.LifeMessageMapper;
+import shop.alien.gateway.mapper.LifeNoticeMapper;
+import shop.alien.gateway.mapper.LifeUserMapper;
+import shop.alien.gateway.util.FunctionMagic;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户
+ */
+@Service
+@RequiredArgsConstructor
+public class LifeUserService extends ServiceImpl<LifeUserMapper, LifeUser> {
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final LifeFansMapper lifeFansMapper;
+
+    private final BaseRedisService baseRedisService;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeMessageMapper messageMapper;
+
+    public IPage<LifeUser> getStoresPage(int page, int size, String realName, String userPhone) {
+        IPage<LifeUser> storePage = new Page<>(page, size);
+        QueryWrapper<LifeUser> queryWrapper = new QueryWrapper<>();
+        queryWrapper.like(!realName.isEmpty(), "real_name", realName);
+        queryWrapper.like(!userPhone.isEmpty(), "user_phone", userPhone);
+        queryWrapper.orderByDesc("created_time");
+        return lifeUserMapper.selectPage(storePage, queryWrapper);
+    }
+
+    public int modifyUser(LifeUser user) {
+        return lifeUserMapper.updateById(user);
+    }
+
+    public int addOrUpdateUser(LifeUser user) {
+        user.setCreatedTime(new Date());
+        return lifeUserMapper.insert(user);
+    }
+
+    public LifeUser getUserById(String id) {
+        return lifeUserMapper.selectById(id);
+    }
+
+    public int addFans(LifeFans fans) {
+        fans.setCreatedTime(new Date());
+        int num = lifeFansMapper.insert(fans);
+
+        if (num == 1) {
+            LifeNotice notice = new LifeNotice();
+            notice.setTitle("关注通知");
+            notice.setContext("关注了你");
+            notice.setNoticeType(0);
+            notice.setReceiverId(fans.getFollowedId());
+            notice.setSenderId(fans.getFansId());
+
+            // 根据手机号查询发送人信息
+            String storePhones = "''";
+            String userPhones = "''";
+            if (fans.getFansId().split("_")[0].equals("store")) {
+                storePhones = "'" + fans.getFansId().split("_")[1] + "'";
+            } else {
+                userPhones = "'" + fans.getFansId().split("_")[1] + "'";
+            }
+            List<LifeMessageVo> userList = messageMapper.getLifeUserAndStoreUserByPhone(storePhones, userPhones);
+            if (!CollectionUtils.isEmpty(userList)) {
+                notice.setBusinessId(userList.get(0).getId());
+            }
+            lifeNoticeMapper.insert(notice);
+        }
+
+        return num;
+    }
+
+    public int cancelFans(LifeFans fans) {
+        LambdaUpdateWrapper<LifeFans> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.eq(LifeFans::getFansId, fans.getFansId());
+        wrapper.eq(LifeFans::getFollowedId, fans.getFollowedId());
+        return lifeFansMapper.delete(wrapper);
+    }
+
+    public IPage<LifeUserVo> getStoreAndUserByName(LifeUserVo vo) {
+        QueryWrapper<LifeUserVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.like(StringUtils.isNotEmpty(vo.getSearchName()), "a.storeUserName", vo.getSearchName());
+        IPage<LifeUserVo> voList = lifeUserMapper.getStoreAndUserByName(new Page<>(vo.getPage(), vo.getSize()), queryWrapper);
+
+        // 查询我的关注
+        LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
+        lifeFansWrapper.eq(LifeFans::getFansId, vo.getPhoneId());
+        List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
+        List<String> followList = lifeFansList.stream().map(LifeFans::getFollowedId).collect(Collectors.toList());
+
+        voList.getRecords().forEach(item -> {
+            if (followList.contains(item.getPhoneId())) {
+                item.setIsFollowThis("1");
+            } else {
+                item.setIsFollowThis("0");
+            }
+        });
+        return voList;
+    }
+
+    public LifeUserVo userLogin(String phoneNum) {
+        LifeUser user = getUserByPhone(phoneNum);
+        if (user == null) {
+            LifeUser lifeUser = new LifeUser();
+            lifeUser.setUserPhone(phoneNum);
+            lifeUser.setUserName(phoneNum);
+            lifeUser.setRealName(phoneNum);
+            lifeUser.setCreatedTime(new Date());
+            int ret = lifeUserMapper.insert(lifeUser);
+            if (ret == 1) {
+                LifeUser user2 = getUserByPhone(phoneNum);
+                LifeUserVo userVo = new LifeUserVo();
+                BeanUtils.copyProperties(user2, userVo);
+                Map<String, String> tokenMap = new HashMap<>();
+                tokenMap.put("phone", phoneNum);
+                tokenMap.put("userName", lifeUser.getUserName());
+                tokenMap.put("userId", lifeUser.getId().toString());
+                tokenMap.put("userType", "user");
+                userVo.setToken(JWTUtils.createToken(tokenMap));
+                baseRedisService.setString("user_" + phoneNum, userVo.getToken());
+                return userVo;
+            } else {
+                return null;
+            }
+        } else {
+            LifeUserVo userVo = new LifeUserVo();
+            BeanUtils.copyProperties(user, userVo);
+            Map<String, String> tokenMap = new HashMap<>();
+            tokenMap.put("phone", phoneNum);
+            tokenMap.put("userName", user.getUserName());
+            tokenMap.put("userId", user.getId().toString());
+            tokenMap.put("userType", "user");
+            String token = JWTUtils.createToken(tokenMap);
+            userVo.setToken(token);
+            baseRedisService.setString("user_" + phoneNum, token);
+            return userVo;
+        }
+    }
+
+    public LifeUser getUserByPhone(String phoneNum) {
+        LambdaQueryWrapper<LifeUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LifeUser::getUserPhone, phoneNum);
+        return this.getOne(lambdaQueryWrapper);
+    }
+
+    public void liftCancelAccount(LifeUser user) {
+        // 通过id获取当前用户账号信息
+        LambdaQueryWrapper<LifeUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LifeUser::getId, user.getId());
+        LifeUser lifeUser = lifeUserMapper.selectOne(lambdaQueryWrapper);
+        // 修改注销标记为1
+        lifeUser.setLogoutFlag(1);
+        // 添加注销原因
+        lifeUser.setLogoutReason(user.getLogoutReason());
+        // 添加注销申请时间
+        lifeUser.setLogoutTime(new Date());
+        lifeUserMapper.updateById(lifeUser);
+    }
+
+    public void liftCancelAccountUn(Integer id) {
+        LambdaQueryWrapper<LifeUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LifeUser::getId, id);
+        LifeUser lifeUser = lifeUserMapper.selectOne(lambdaQueryWrapper);
+        // 修改注销标记为0
+        lifeUser.setLogoutFlag(0);
+        // 清空注销原因
+        lifeUser.setLogoutReason(null);
+        // 清空注销申请时间
+        lifeUser.setLogoutTime(null);
+        lifeUserMapper.updateById(lifeUser);
+    }
+
+    public List<String> getIds(String name, String phone) {
+        if(Objects.isNull(name) && Objects.isNull(phone)) return Arrays.asList("");
+        LambdaQueryWrapper<LifeUser> wrapper = new LambdaQueryWrapper<>();
+        List<Triple<Boolean, SFunction<LifeUser, ?>, Object>> conditions = new ArrayList<>();
+        conditions.add(Triple.of(StringUtils.isNotEmpty(name), LifeUser::getUserName, name));
+        conditions.add(Triple.of(StringUtils.isNotEmpty(phone), LifeUser::getUserPhone, phone));
+        FunctionMagic.buildLikeConditions(wrapper, conditions);
+        List<String> res = FunctionMagic.convertToIds(lifeUserMapper.selectList(wrapper),LifeUser::getId);
+        return CollectionUtils.isEmpty(res) ? Arrays.asList("") : res;
+    }
+}

+ 61 - 0
alien-gateway/src/main/java/shop/alien/gateway/util/FileUpload.java

@@ -0,0 +1,61 @@
+package shop.alien.gateway.util;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+@Configuration
+public class FileUpload {
+
+    @Value("${spring.web.resources.static-locations}")
+    private String uploadDir;
+
+    public String saveImage(MultipartFile image) {
+        try {
+            String cleanUploadDir = uploadDir.replace("file:///", "").replace("\\", "/");
+            Path path = Paths.get(cleanUploadDir, image.getOriginalFilename());
+            Files.createDirectories(path.getParent());
+            Files.write(path, image.getBytes());
+            return image.getOriginalFilename();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+//    public String getVideoDuration(MultipartFile image) {
+//        String cleanUploadDir = uploadDir.replace("file:///", "").replace("\\", "/");
+//        Path path = Paths.get(cleanUploadDir, image.getOriginalFilename());
+//        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(path.toString())) {
+//            String fileExtension = getFileExtension(path.toString());
+//            if ("mp4".equalsIgnoreCase(fileExtension)) {
+//                grabber.setFormat("mp4");
+//            } else if ("avi".equalsIgnoreCase(fileExtension)) {
+//                grabber.setFormat("avi");
+//            } else if ("mkv".equalsIgnoreCase(fileExtension)) {
+//                grabber.setFormat("matroska");
+//            }
+//            grabber.start();
+//            double durationInSeconds = grabber.getLengthInTime() / 1_000_000.0;
+//            grabber.stop();
+//            long hours = (long) durationInSeconds / 3600;
+//            long minutes = (long) (durationInSeconds % 3600) / 60;
+//            long seconds = (long) (durationInSeconds % 60);
+//            return String.format("%dh %dm %ds", hours, minutes, seconds);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            return "获取时长失败";
+//        }
+//    }
+
+//    private String getFileExtension(String filePath) {
+//        File file = new File(filePath);
+//        String name = file.getName();
+//        int lastIndexOfDot = name.lastIndexOf('.');
+//        return (lastIndexOfDot == -1) ? "" : name.substring(lastIndexOfDot + 1);
+//    }
+}

+ 315 - 0
alien-gateway/src/main/java/shop/alien/gateway/util/FunctionMagic.java

@@ -0,0 +1,315 @@
+package shop.alien.gateway.util;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import org.apache.commons.lang3.tuple.Triple;
+import org.springframework.data.geo.Distance;
+import org.springframework.data.geo.GeoResult;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.store.entity.dto.NearMeDto;
+import shop.alien.store.util.constant.Constant;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.Collectors;
+
+/**
+ * Magic wand
+ */
+public class FunctionMagic {
+
+    /**
+     * Magic for
+     * @param list
+     * @param processor
+     * @param <T>
+     */
+    public static <T> void processList (List<T>list , Consumer<T> processor){
+        Optional.ofNullable(list).ifPresent(l -> l.forEach(processor));
+    }
+
+
+    /**
+     * Magic default for
+     * @param list
+     * @param processor
+     * @param defaultSupplier
+     * @param <T>
+     */
+
+    public static <T> void processList(List<T> list, Consumer<T> processor, Supplier<List<T>> defaultSupplier) {
+        List<T> target = CollectionUtils.isEmpty(list) ? defaultSupplier.get() : list;
+        target.forEach(processor);
+    }
+
+
+    /**
+     * Magic for GeoResult
+     * @param targetList
+     * @return
+     * @param <T>
+     */
+    public static <T> Consumer<GeoResult<RedisGeoCommands.GeoLocation<T>>> createGeoMagic(List<T> targetList) {
+        return geoResult ->
+                Optional.ofNullable(geoResult).
+                map(GeoResult::getContent).
+                map(RedisGeoCommands.GeoLocation::getName).
+                ifPresent(targetList::add);
+    }
+
+
+    /**
+     * Magic for NearMeDto
+     * @param targetList
+     * @return
+     * @param
+     */
+    public static Consumer<GeoResult<RedisGeoCommands.GeoLocation<String>>> createNearMeDtoConsumer(List<NearMeDto> targetList) {
+        return geoResult -> Optional.ofNullable(geoResult).
+                map(GeoResult::getContent).
+                ifPresent(content -> {
+                    String name = content.getName();
+                    Distance distance = geoResult.getDistance();
+                    if (isNonNull(name).test(distance)) targetList.add(new NearMeDto(name, distance.getValue()));
+                });
+    }
+
+    /**
+     * Magic isNonNull
+     * @param var2
+     * @return
+     * @param <T>
+     * @param <R>
+     */
+    public static <T, R> Predicate<T> isNonNull(R var2) {
+        return var1 -> Objects.nonNull(var1) && Objects.nonNull(var2);
+    }
+
+
+
+    /**
+     * Magic isEmptyMagic
+     * @param valueToCheck
+     * @param columnExtractor
+     * @param queryExecutor
+     * @return
+     * @param <T>
+     * @param <R>
+     * @param <V>
+     */
+    public static <T, R, V> boolean isEmptyMagic(
+            V valueToCheck,
+            SFunction<T, R> columnExtractor,
+            SFunction<LambdaQueryWrapper<T>, T> queryExecutor) {
+
+        if (valueToCheck == null) return true;
+
+        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
+        wrapper.select(columnExtractor);
+        wrapper.eq(columnExtractor, valueToCheck);
+        wrapper.last("LIMIT 1");
+
+        T entity = queryExecutor.apply(wrapper);
+        R columnValue = Optional.ofNullable(entity).map(columnExtractor).orElse(null);
+        return columnValue != null;
+    }
+
+
+    /**
+     * Magic buildLikeConditions
+     * @param wrapper
+     * @param conditions
+     * @param <T>
+     */
+    public static <T> void buildLikeConditions(
+            LambdaQueryWrapper<T> wrapper,
+            List<Triple<Boolean, SFunction<T, ?>, Object>> conditions){
+        conditions.forEach(condition -> wrapper.like(condition.getLeft(), condition.getMiddle(), condition.getRight()));
+    }
+
+    /**
+     * Magic convertToIds
+     * @param list
+     * @param idExtractor
+     * @return
+     * @param <T>
+     */
+    public static <T> List<String> convertToIds(
+            List<T> list,
+            Function<T, ?> idExtractor){
+        return Optional.ofNullable(list).
+                orElse(Collections.emptyList()).
+                stream().map(idExtractor).
+                filter(Objects::nonNull).
+                map(Object::toString).
+                collect(Collectors.toList());
+    }
+
+    /**
+     * Magic isListFlag
+     * @param list
+     * @param list1
+     * @return
+     * @param <T>
+     */
+
+    public static <T> boolean isListFlag(
+            List<T> list,
+            List<T> list1
+            ){
+        return !(Objects.equals(list.get(0), "") && Objects.equals(list1.get(0), ""));
+
+    }
+
+    /**
+     * Magic populateUserInfo
+     * @param dto
+     * @param userType
+     * @param userId
+     * @param userQuery
+     * @param nameSetter
+     * @param phoneSetter
+     * @param <T>
+     * @param <D>
+     */
+    public static <T, D> void populateUserInfo(
+            D dto,
+            String userType,
+            String userId,
+            Function<String, T> userQuery,
+            BiConsumer<D, String> nameSetter,
+            BiConsumer<D, String> phoneSetter) {
+
+        T user = Optional.ofNullable(userId)
+                .map(userQuery)
+                .orElseThrow(() -> new RuntimeException("User not found"));
+
+        if("1".equals(userType)) {
+            // M类型处理逻辑
+            if(user instanceof StoreUser) {
+                StoreUser storeUser = (StoreUser) user;
+                nameSetter.accept(dto, storeUser.getName());
+                phoneSetter.accept(dto, storeUser.getPhone());
+            }
+        } else {
+            // U类型处理逻辑
+            if(user instanceof LifeUser) {
+                LifeUser lifeUser = (LifeUser) user;
+                nameSetter.accept(dto, lifeUser.getUserName());
+                phoneSetter.accept(dto, lifeUser.getUserPhone());
+            }
+        }
+    }
+
+
+    /**
+     * Magic  handleUserInfo
+     * @param userType
+     * @param userId
+     * @param storeQuery
+     * @param lifeQuery
+     * @param consumer
+     * @param <T>
+     */
+
+    public static <T> void handleUserInfo(
+            String userType,
+            String userId,
+            Function<String, T> storeQuery,
+            Function<String, T> lifeQuery,
+            Consumer<T> consumer) {
+        if (userId == null) return;
+        T user = Constant.SEQUENCE_1.equals(userType) ?
+                storeQuery.apply(userId) :
+                lifeQuery.apply(userId);
+        if (user != null) {
+            consumer.accept(user);
+        }
+    }
+
+
+    /**
+     * Magic idQueryWrapper
+     * @param userId
+     * @return
+     * @param <T>
+     */
+    public static <T> QueryWrapper<T > idQueryWrapper(String userId) {
+        return new QueryWrapper<T>().eq("id", userId).last("OR (delete_flag = 1 AND id = " + userId + ") limit 1"); // 强制包含已删除的记录;
+    }
+
+
+    // 举报内容
+    private static final Map<String, String> CONTEXT = new HashMap<>();
+    static {
+        CONTEXT.put("1", "用户");
+        CONTEXT.put("2", "动态");
+        CONTEXT.put("3", "评论");
+    }
+
+
+    // 举报理由
+    private static final Map<String, String> VIOLATION = new HashMap<>();
+    static {
+        VIOLATION.put("1", "用户违规");
+        VIOLATION.put("2", "色情低俗");
+        VIOLATION.put("3", "违法违规");
+        VIOLATION.put("4", "谩骂嘲讽、煽动对立");
+        VIOLATION.put("5", "涉嫌诈骗");
+        VIOLATION.put("6", "人身攻击");
+        VIOLATION.put("7", "种族歧视");
+        VIOLATION.put("8", "政治敏感");
+        VIOLATION.put("9", "虚假、不实内容、违反公德秩序");
+        VIOLATION.put("10", "违反公德秩序");
+        VIOLATION.put("11", "危害人身安全");
+        VIOLATION.put("12", "网络暴力");
+        VIOLATION.put("13", "其他原因");
+    }
+
+
+    // 状态
+    private static final Map<String, String> STATUS = new HashMap<>();
+    static {
+        STATUS.put("0", "待处理");
+        STATUS.put("1", "已通过");
+        STATUS.put("2", "已驳回");
+    }
+
+
+    public static String getByCode(String statusCode, Map<String, String> mapping) {
+        return Optional.ofNullable(mapping.get(statusCode))
+                .orElseThrow(() -> new IllegalArgumentException("非法状态码: " + statusCode));
+    }
+
+
+    // 举报内容
+    public static String getContext(String statusCode) {
+        return getByCode(statusCode, CONTEXT);
+    }
+
+    // 举报理由
+    public static String violation(String statusCode) {
+        return getByCode(statusCode, VIOLATION);
+    }
+
+
+
+    // 状态
+    public static String status(String statusCode) {
+        return getByCode(statusCode, STATUS);
+    }
+
+
+
+
+
+
+
+
+
+
+}

+ 69 - 0
alien-gateway/src/main/java/shop/alien/gateway/util/RandomCreateUtil.java

@@ -0,0 +1,69 @@
+package shop.alien.gateway.util;
+
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+/**
+ * 生成随机数工具类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/12 15:17
+ */
+@Component
+public class RandomCreateUtil {
+
+    /**
+     * 生成随机数
+     *
+     * @param length 位数
+     * @return 随机数
+     */
+    public String getRandomNum(Integer length) {
+        List<String> list = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
+        Collections.shuffle(list);
+        List<String> collect = list.stream().limit(length).collect(Collectors.toList());
+        StringBuilder stringBuilder = new StringBuilder();
+        collect.forEach(stringBuilder::append);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * 生成随机数
+     *
+     * @param min 最小值
+     * @param max 最大值
+     * @return 随机数
+     */
+    public Integer getRandomNum(Integer min, Integer max) {
+        Random random = new Random();
+        return random.nextInt((max - min) + 1) + min;
+    }
+
+    /**
+     * 生成随机字符串
+     *
+     * @param length 长度
+     * @return 字符串
+     */
+    public String getRandomNStr(Integer length) {
+        StringBuilder sb = new StringBuilder();
+        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        for (int i = 0; i < length; i++) {
+            int index = ThreadLocalRandom.current().nextInt(chars.length());
+            sb.append(chars.charAt(index));
+        }
+        return sb.toString();
+    }
+
+    public static void main(String[] args) {
+        RandomCreateUtil randomCreateUtil = new RandomCreateUtil();
+        System.out.println(randomCreateUtil.getRandomNStr(20));
+    }
+}