开启左侧

Redis完成微博好友功能微服务(关注,取关,共同关注)

[复制链接]
在线会员 ymuUw7L16pYA 发表于 2023-1-4 00:18:09 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
需要阐发

密友功用是今朝交际场景的必备功用之一,一般密友相干的功用包罗有:存眷/与闭、尔(他)的存眷、尔(他)的粉丝、配合存眷、尔存眷的人也存眷他等如许一点儿功用。

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-1.jpg
类似于如许的功用咱们假设接纳数据库干的话不过纯真获得用户的一点儿粉丝大概存眷列表的话是很简朴也很简单完毕, 可是假设尔念要查出二个以至少个用户配合存眷了哪些人大概念要盘问二个大概多个用户的配合粉丝的话便会很省事, 服从也没有会很下。可是假设您用redis来干的话便会相称的简朴并且服从很下。启事是redis自己自己戴有特地针关于这类汇合的交加、并散、好散的一点儿操纵。

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-2.jpg
设想思路

整体思路咱们接纳MySQL + Redis的方法分离完毕。MySQL主要是保留降天数据,而使用Redis的Sets数据范例截至汇合操纵。Sets具有来沉(咱们不克不及屡次存眷统一用户)功用。一个用户咱们存贮二个汇合:一个是保留用户存眷的人 另外一个是保留存眷用户的人。

  • SADD 增加成员;号令格局: SADD key member [member …] ----- 存眷
  • SREM 移除某个成员;号令格局: SREM key member [member …] -------与闭
  • SCARD 统计汇合内乱的成员数;号令格局: SCARD key -------存眷/粉丝个数
  • SISMEMBER 鉴别可否是汇合成员;号令格局:SISMEMBER key member ---------鉴别可否存眷(假设存眷那末只能以面打与闭)
  • SMEMBERS 盘问汇合内乱的成员;号令格局: SMEMBERS key -------列表使用(存眷列表战粉丝列表)
  • SINTER 盘问汇合的交加;号令格局: SINTER key [key …] --------配合存眷、尔存眷的人存眷了他
  • 需要保守邪版IDEA的能够联系尔,79元一年,邪版受权,民网可查有用期,有需要的减尔微疑:poxiaozhiai6,备注:126。
数据库表设想

那个数据库表的构造比力简朴,主要记载了用户id、用户存眷的id战存眷形状。
  1. CREATE TABLE `t_follow` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,
  3.   `user_id` int(11) DEFAULT NULL COMMENT '目前登任命户的id',
  4.   `follow_user_id` int(11) DEFAULT NULL COMMENT '目前登任命户存眷的用户的id',
  5.   `is_valid` tinyint(1) DEFAULT NULL COMMENT '存眷形状,0-不存眷,1-存眷了',
  6.   `create_date` datetime DEFAULT NULL,
  7.   `update_date` datetime DEFAULT NULL,
  8.   PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户战用户存眷表';
复造代码
新修密友功用微效劳

增加依靠战设置

pom依靠以下:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <parent>
  6.         <artifactId>redis-seckill</artifactId>
  7.         <groupId>com.zjq</groupId>
  8.         <version>1.0-SNAPSHOT</version>
  9.     </parent>
  10.     <modelVersion>4.0.0</modelVersion>
  11.     <artifactId>ms-follow</artifactId>
  12.     <dependencies>
  13.         <!-- eureka client -->
  14.         <dependency>
  15.             <groupId>org.springframework.cloud</groupId>
  16.             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  17.         </dependency>
  18.         <!-- spring web -->
  19.         <dependency>
  20.             <groupId>org.springframework.boot</groupId>
  21.             <artifactId>spring-boot-starter-web</artifactId>
  22.         </dependency>
  23.         <!-- mysql -->
  24.         <dependency>
  25.             <groupId>mysql</groupId>
  26.             <artifactId>mysql-connector-java</artifactId>
  27.         </dependency>
  28.         <!-- spring data redis -->
  29.         <dependency>
  30.             <groupId>org.springframework.boot</groupId>
  31.             <artifactId>spring-boot-starter-data-redis</artifactId>
  32.         </dependency>
  33.         <!-- mybatis -->
  34.         <dependency>
  35.             <groupId>org.mybatis.spring.boot</groupId>
  36.             <artifactId>mybatis-spring-boot-starter</artifactId>
  37.         </dependency>
  38.         <!-- co妹妹ons 大众名目 -->
  39.         <dependency>
  40.             <groupId>com.zjq</groupId>
  41.             <artifactId>co妹妹ons</artifactId>
  42.             <version>1.0-SNAPSHOT</version>
  43.         </dependency>
  44.         <!-- swagger -->
  45.         <dependency>
  46.             <groupId>com.battcn</groupId>
  47.             <artifactId>swagger-spring-boot-starter</artifactId>
  48.         </dependency>
  49.     </dependencies>
  50. </project>
复造代码
springboot设置以下:
  1. server:
  2.   port: 7004 # 端心
  3. spring:
  4.   application:
  5.     name: ms-follow # 使用名
  6.   # 数据库
  7.   datasource:
  8.     driver-class-name: com.mysql.cj.jdbc.Driver
  9.     username: root
  10.     password: root
  11.     url: jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
  12.   # Redis
  13.   redis:
  14.     port: 6379
  15.     host: localhost
  16.     timeout: 3000
  17.     password: 123456
  18.     database: 2
  19.   # Swagger
  20.   swagger:
  21.     base-package: com.zjq.follow
  22.     title: 佳勤奋能微效劳API交心文档
  23. # 设置 Eureka Server 备案中间
  24. eureka:
  25.   instance:
  26.     prefer-ip-address: true
  27.     instance-id: ${spring.cloud.client.ip-address}:${server.port}
  28.   client:
  29.     service-url:
  30.       defaultZone: http://localhost:7000/eureka/
  31. service:
  32.   name:
  33.     ms-oauth-server: http://ms-oauth2-server/
  34.     ms-diners-server: http://ms-users/
  35. mybatis:
  36.   configuration:
  37.     map-underscore-to-camel-case: true # 启开驼峰映照
  38. logging:
  39.   pattern:
  40.     console: '%d{HH:妹妹:ss} [%thread] %-5level %logger{50} - %msg%n'
复造代码
增加设置类

redis设置类:
  1. package com.zjq.seckill.config;
  2. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  3. import com.fasterxml.jackson.annotation.PropertyAccessor;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.core.io.ClassPathResource;
  8. import org.springframework.data.redis.connection.RedisConnectionFactory;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.core.script.DefaultRedisScript;
  11. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  12. import org.springframework.data.redis.serializer.StringRedisSerializer;
  13. /**
  14. * RedisTemplate设置类
  15. * @author zjq
  16. */
  17. @Configuration
  18. public class RedisTemplateConfiguration {
  19.     /**
  20.      * redisTemplate 序列化使用的jdkSerializeable, 保存两退造字节码, 以是自界说序列化类
  21.      *
  22.      * @param redisConnectionFactory
  23.      * @return
  24.      */
  25.     @Bean
  26.     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  27.         RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
  28.         redisTemplate.setConnectionFactory(redisConnectionFactory);
  29.         // 使用Jackson2JsonRedisSerialize交流 默认序列化
  30.         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  31.         ObjectMapper objectMapper = new ObjectMapper();
  32.         objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  33.         jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  34.         // 树立key战value的序列化划定规矩
  35.         redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
  36.         redisTemplate.setKeySerializer(new StringRedisSerializer());
  37.         redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  38.         redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
  39.         redisTemplate.afterPropertiesSet();
  40.         return redisTemplate;
  41.     }
  42.    
  43. }
复造代码
REST设置类:

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-3.jpg
存眷/与闭完毕

营业逻辑


Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-4.jpg
Mapper完毕

Mapper比力简朴主要是盘问存眷疑息、增加存眷疑息、与闭大概再次存眷。

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-5.jpg
Service层完毕
  1. package com.zjq.seckill.service;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import com.zjq.co妹妹ons.constant.ApiConstant;
  4. import com.zjq.co妹妹ons.constant.RedisKeyConstant;
  5. import com.zjq.co妹妹ons.exception.ParameterException;
  6. import com.zjq.co妹妹ons.model.domain.ResultInfo;
  7. import com.zjq.co妹妹ons.model.pojo.Follow;
  8. import com.zjq.co妹妹ons.model.vo.SignInUserInfo;
  9. import com.zjq.co妹妹ons.utils.AssertUtil;
  10. import com.zjq.co妹妹ons.utils.ResultInfoUtil;
  11. import com.zjq.seckill.mapper.FollowMapper;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.data.redis.core.RedisTemplate;
  14. import org.springframework.stereotype.Service;
  15. import org.springframework.web.client.RestTemplate;
  16. import javax.annotation.Resource;
  17. import java.util.LinkedHashMap;
  18. /**
  19. * 存眷/与闭营业逻辑层
  20. * @author zjq
  21. */
  22. @Service
  23. public class FollowService {
  24.     @Value("${service.name.ms-oauth-server}")
  25.     private String oauthServerName;
  26.     @Value("${service.name.ms-diners-server}")
  27.     private String dinersServerName;
  28.     @Resource
  29.     private RestTemplate restTemplate;
  30.     @Resource
  31.     private FollowMapper followMapper;
  32.     @Resource
  33.     private RedisTemplate redisTemplate;
  34.     /**
  35.      * 存眷/与闭
  36.      *
  37.      * @param followUserId 存眷的门客ID
  38.      * @param isFollowed    可否存眷 1=存眷 0=与闭
  39.      * @param accessToken   登任命户token
  40.      * @param path         拜访 地点
  41.      * @return
  42.      */
  43.     public ResultInfo follow(Integer followUserId, int isFollowed,
  44.                              String accessToken, String path) {
  45.         // 可否挑选了存眷工具
  46.         AssertUtil.isTrue(followUserId == null || followUserId < 1,
  47.                 "请挑选要存眷的人");
  48.         // 获得登任命户疑息 (启拆办法)
  49.         SignInUserInfo dinerInfo = loadSignInDinerInfo(accessToken);
  50.         // 获得目前登任命户取需要存眷用户的存眷疑息
  51.         Follow follow = followMapper.selectFollow(dinerInfo.getId(), followUserId);
  52.         //假设 不存眷疑息,且要截至存眷操纵 -- 增加存眷
  53.         if (follow == null && isFollowed == 1) {
  54.             // 增加存眷疑息
  55.             int count = followMapper.save(dinerInfo.getId(), followUserId);
  56.             // 增加存眷列表到 Redis
  57.             if (count == 1) {
  58.                 addToRedisSet(dinerInfo.getId(), followUserId);
  59.             }
  60.             return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
  61.                     "存眷胜利", path, "存眷胜利");
  62.         }
  63.         //假设 相关注疑息,且今朝处于存眷形状,且要截至与闭操纵 -- 与闭存眷
  64.         if (follow != null && follow.getIsValid() == 1 && isFollowed == 0) {
  65.             // 与闭
  66.             int count = followMapper.update(follow.getId(), isFollowed);
  67.             // 移除 Redis 存眷列表
  68.             if (count == 1) {
  69.                 removeFromRedisSet(dinerInfo.getId(), followUserId);
  70.             }
  71.             return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
  72.                     "胜利与闭", path, "胜利与闭");
  73.         }
  74.         //假设 相关注疑息,且今朝处于与闭形状,且要截至存眷操纵 -- 从头存眷
  75.         if (follow != null && follow.getIsValid() == 0 && isFollowed == 1) {
  76.             // 从头存眷
  77.             int count = followMapper.update(follow.getId(), isFollowed);
  78.             // 增加存眷列表到 Redis
  79.             if (count == 1) {
  80.                 addToRedisSet(dinerInfo.getId(), followUserId);
  81.             }
  82.             return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE,
  83.                     "存眷胜利", path, "存眷胜利");
  84.         }
  85.         return ResultInfoUtil.buildSuccess(path, "操纵胜利");
  86.     }
  87.     /**
  88.      * 增加存眷列表到 Redis
  89.      *
  90.      * @param dinerId
  91.      * @param followUserId
  92.      */
  93.     private void addToRedisSet(Integer dinerId, Integer followUserId) {
  94.         redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followUserId);
  95.         redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followUserId, dinerId);
  96.     }
  97.     /**
  98.      * 移除 Redis 存眷列表
  99.      *
  100.      * @param dinerId
  101.      * @param followUserId
  102.      */
  103.     private void removeFromRedisSet(Integer dinerId, Integer followUserId) {
  104.         redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followUserId);
  105.         redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followUserId, dinerId);
  106.     }
  107.     /**
  108.      * 获得登任命户疑息
  109.      *
  110.      * @param accessToken
  111.      * @return
  112.      */
  113.     private SignInUserInfo loadSignInDinerInfo(String accessToken) {
  114.         //必需 登录
  115.         AssertUtil.mustLogin(accessToken);
  116.         String url = oauthServerName + "user/me?access_token={accessToken}";
  117.         ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
  118.         if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
  119.             throw new ParameterException(resultInfo.getMessage());
  120.         }
  121.         SignInUserInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
  122.                 new SignInUserInfo(), false);
  123.         return dinerInfo;
  124.     }
  125. }
复造代码
Controller完毕
  1. package com.zjq.seckill.controller;
  2. import com.zjq.co妹妹ons.model.domain.ResultInfo;
  3. import com.zjq.seckill.service.FollowService;
  4. import org.springframework.web.bind.annotation.*;
  5. import javax.annotation.Resource;
  6. import javax.servlet.http.HttpServletRequest;
  7. /**
  8. * 存眷/与闭掌握层
  9. * @author zjq
  10. */
  11. @RestController
  12. public class FollowController {
  13.     @Resource
  14.     private FollowService followService;
  15.     @Resource
  16.     private HttpServletRequest request;
  17.     /**
  18.      * 存眷/与闭
  19.      *
  20.      * @param followUserId 存眷的用户ID
  21.      * @param isFollowed    可否存眷 1=存眷 0=打消
  22.      * @param access_token  登任命户token
  23.      * @return
  24.      */
  25.     @PostMapping("/{followUserId}")
  26.     public ResultInfo follow(@PathVariable Integer followUserId,
  27.                              @RequestParam int isFollowed,
  28.                              String access_token) {
  29.         ResultInfo resultInfo = followService.follow(followUserId,
  30.                 isFollowed, access_token, request.getServletPath());
  31.         return resultInfo;
  32.     }
  33. }
复造代码
网闭设置路由划定规矩
  1. spring:
  2.   application:
  3.     name: ms-gateway
  4.   cloud:
  5.     gateway:
  6.       discovery:
  7.         locator:
  8.           enabled: true # 启开设置备案中间截至路由功用
  9.           lower-case-service-id: true # 将效劳称呼转小写
  10.       routes:
  11.          
  12.          # 密友功用微效劳
  13.         - id: ms-follow
  14.           uri: lb://ms-follow
  15.           predicates:
  16.             - Path=/follow/**
  17.           filters:
  18.             - StripPrefix=1
复造代码
测尝试证

顺次启用,备案中间、网闭、认证中间、密友功用微效劳。
尝试id为5的用户,存眷id为1的用户。

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-6.jpg
检查 redis能够瞅到有二个汇合,一个粉丝汇合,一个存眷汇合。

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-7.jpg
检察数据库,id为5的用户存眷了id为1的用户

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-8.jpg
让id即是7的用户存眷id即是1的用户,redis战数据库保存疑息以下:

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-9.jpg
配合存眷列表


  • 从Redis中读与登任命户的存眷列表取检察用户的存眷列表,而后截至交加操纵,获得配合存眷的用户id
  • 而后颠末用户效劳传进用户id数据获得用户根本疑息
Controller增加办法
  1.     /**
  2.      * 配合存眷列表
  3.      *
  4.      * @param userId
  5.      * @param access_token
  6.      * @return
  7.      */
  8.     @GetMapping("co妹妹ons/{userId}")
  9.     public ResultInfo findCo妹妹onsFriends(@PathVariable Integer userId,
  10.                                          String access_token) {
  11.         return followService.findCo妹妹onsFriends(userId, access_token, request.getServletPath());
  12.     }
复造代码
Service增加办法
  1.     /**
  2.      * 配合存眷列表
  3.      *
  4.      * @param userId
  5.      * @param accessToken
  6.      * @param path
  7.      * @return
  8.      */
  9.     @Transactional(rollbackFor = Exception.class)
  10.     public ResultInfo findCo妹妹onsFriends(Integer userId, String accessToken, String path) {
  11.         // 可否挑选了检察工具
  12.         AssertUtil.isTrue(userId == null || userId < 1,
  13.                 "请挑选要检察的人");
  14.         // 获得登任命户疑息
  15.         SignInUserInfo userInfo = loadSignInuserInfo(accessToken);
  16.         // 获得登任命户的存眷疑息
  17.         String loginuserKey = RedisKeyConstant.following.getKey() + userInfo.getId();
  18.         // 获得登任命户检察工具的存眷疑息
  19.         String userKey = RedisKeyConstant.following.getKey() + userId;
  20.         // 计较交加
  21.         Set<Integer> userIds = redisTemplate.opsForSet().intersect(loginuserKey, userKey);
  22.         // 不
  23.         if (userIds == null || userIds.isEmpty()) {
  24.             return ResultInfoUtil.buildSuccess(path, new ArrayList<ShortUserInfo>());
  25.         }
  26.         // 挪用门客效劳按照 ids 盘问门客疑息
  27.         ResultInfo resultInfo = restTemplate.getForObject(usersServerName + "findByIds?access_token={accessToken}&ids={ids}",
  28.                 ResultInfo.class, accessToken, StrUtil.join(",", userIds));
  29.         if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
  30.             resultInfo.setPath(path);
  31.             return resultInfo;
  32.         }
  33.         //处置 成果散
  34.         List<LinkedHashMap> dinnerInfoMaps = (ArrayList) resultInfo.getData();
  35.         List<ShortUserInfo> userInfos = dinnerInfoMaps.stream()
  36.                 .map(user -> BeanUtil.fillBeanWithMap(user, new ShortUserInfo(), true))
  37.                 .collect(Collectors.toList());
  38.         return ResultInfoUtil.buildSuccess(path, userInfos);
  39.     }
复造代码
用户效劳新删按照ids盘问用户汇合

Controller:
  1.     /**
  2.      *依据 ids 盘问用户疑息
  3.      *
  4.      * @param ids
  5.      * @return
  6.      */
  7.     @GetMapping("findByIds")
  8.     public ResultInfo<List<ShortUserInfo>> findByIds(String ids) {
  9.         List<ShortUserInfo> dinerInfos = userService.findByIds(ids);
  10.         return ResultInfoUtil.buildSuccess(request.getServletPath(), dinerInfos);
  11.     }
复造代码
Service:
  1.     /**
  2.      *依据 ids 盘问门客疑息
  3.      *
  4.      * @param ids 主键 id,多个以逗号分开,逗号之间不消空格
  5.      * @return
  6.      */
  7.     public List<ShortUserInfo> findByIds(String ids) {
  8.         AssertUtil.isNotEmpty(ids);
  9.         String[] idArr = ids.split(",");
  10.         List<ShortUserInfo> dinerInfos = usersMapper.findByIds(idArr);
  11.         return dinerInfos;
  12.     }
复造代码
Mapper:
  1.     /**
  2.      *依据 ID 汇合盘问多个门客疑息
  3.      * @param ids
  4.      * @return
  5.      */
  6.     @Select("<script> " +
  7.             " select id, nickname, avatar_url from t_diners " +
  8.             " where is_valid = 1 and id in " +
  9.             " <foreach item=\"id\" collection=\"ids\" open=\"(\" separator=\",\" close=\")\"> " +
  10.             "   #{id} " +
  11.             " </foreach> " +
  12.             " </script>")
  13.     List<ShortUserInfo> findByIds(@Param("ids") String[] ids);
复造代码
上面尝试已经让id5战7的用户存眷了id为1的用户,咱们持续让id5的用户存眷id为3的用户,让id五、六、7的用户存眷了id为2的用户:
redis战数据库疑息以下:

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-10.jpg

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-11.jpg

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-12.jpg

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-13.jpg
测尝试证

盘问目前登任命户id为5战id为7的配合存眷疑息:

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-14.jpg
盘问目前登任命户id为6战id为7的配合存眷疑息:

Redis完毕微专密友功用微效劳(存眷,与闭,配合存眷)-15.jpg
能够瞅进去5战7配合存眷了1战2,6战7只配合存眷了2,契合预期。
原文实质到此完毕了
若有收获 欢送面赞
您的鼓舞是尔最年夜的能源
若有毛病❌疑义欢送 列位指出
您需要登录后才可以回帖 登录 | 立即注册 qq_login

本版积分规则

发布主题
阅读排行更多+
用专业创造成效
400-778-7781
周一至周五 9:00-18:00
意见反馈:server@mailiao.group
紧急联系:181-67184787
ftqrcode

扫一扫关注我们

Powered by 职贝云数A新零售门户 X3.5© 2004-2025 职贝云数 Inc.( 蜀ICP备2024104722号 )