Parcourir la source

元宇宙相关

sunkean il y a 3 ans
Parent
commit
e39f4f9b5b

+ 5 - 0
pom.xml

@@ -481,6 +481,11 @@
             <artifactId>bucket4j-core</artifactId>
             <version>7.5.0</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 15 - 0
src/main/java/com/izouma/nineth/config/WebSocketConfig.java

@@ -0,0 +1,15 @@
+package com.izouma.nineth.config;
+
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+public class WebSocketConfig {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 28 - 0
src/main/java/com/izouma/nineth/domain/MetaAdvertRecord.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.domain;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙广告")
+public class MetaAdvertRecord extends BaseEntity {
+
+    @ApiModelProperty("广告图片")
+    private String pic;
+
+    @ApiModelProperty("链接地址")
+    private String link;
+
+    @ApiModelProperty("是否使用")
+    private boolean used;
+
+}

+ 60 - 0
src/main/java/com/izouma/nineth/domain/MetaMMOLoginInfo.java

@@ -0,0 +1,60 @@
+package com.izouma.nineth.domain;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "meta_mmo_login_info")
+@ApiModel("元宇宙玩家登陆信息")
+@Builder
+public class MetaMMOLoginInfo extends BaseEntity {
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty("用户id")
+    private Long  userId;
+
+    @ApiModelProperty("区域id")
+    private Long  regionId;
+
+    @ApiModelProperty("城市id")
+    private Long  cityId;
+
+    @ApiModelProperty("上线时间")
+    private LocalDateTime onLineTime;
+
+    @ApiModelProperty("离线时间")
+    private LocalDateTime offLineTime;
+
+    @ApiModelProperty("UUID")
+    private String UUID;
+
+    @ApiModelProperty("角色")
+    private String role;
+
+    private Float axisX;
+
+    private Float axisY;
+
+    private Float axisZ;
+
+    private Float eulerX;
+
+    private Float eulerY;
+
+    private Float eulerZ;
+
+}

+ 29 - 0
src/main/java/com/izouma/nineth/dto/MMOMessage.java

@@ -0,0 +1,29 @@
+package com.izouma.nineth.dto;
+
+import com.izouma.nineth.domain.MetaMMOLoginInfo;
+import io.swagger.annotations.ApiModel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("单个消息")
+public class MMOMessage {
+
+    /**
+     * 1.地图玩家信息
+     * 2.移动
+     * 3.下线
+     * 4.玩家加入
+     * 5.自生位置信息
+     */
+    private Integer messageType;
+
+    private MetaMMOLoginInfo message;
+
+    private List<MetaMMOLoginInfo> map;
+}

+ 20 - 0
src/main/java/com/izouma/nineth/dto/MMOSingleMessage.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("单个消息")
+public class MMOSingleMessage {
+
+    @ApiModelProperty("消息类型")
+    private Integer messageType;
+
+    @ApiModelProperty("消息体")
+    private String message;
+}

+ 18 - 0
src/main/java/com/izouma/nineth/repo/MetaAdvertRecordRepo.java

@@ -0,0 +1,18 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaAdvertRecord;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface MetaAdvertRecordRepo extends JpaRepository<MetaAdvertRecord, Long>, JpaSpecificationExecutor<MetaAdvertRecord> {
+    @Query("update MetaAdvertRecord t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    MetaAdvertRecord findByUsedAndDel(boolean used, boolean del);
+}

+ 14 - 0
src/main/java/com/izouma/nineth/repo/MetaMMOLoginInfoRepo.java

@@ -0,0 +1,14 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaMMOLoginInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+
+public interface MetaMMOLoginInfoRepo extends JpaRepository<MetaMMOLoginInfo, Long>, JpaSpecificationExecutor<MetaMMOLoginInfo> {
+
+    @Query(value = "select * from meta_mmo_login_info a where a.user_id = ?1 order by a.created_at desc limit 1",nativeQuery = true)
+    MetaMMOLoginInfo findLastByUserId(Long userId);
+
+    MetaMMOLoginInfo findByUUID(String UUID);
+}

+ 2 - 0
src/main/java/com/izouma/nineth/security/WebSecurityConfig.java

@@ -148,6 +148,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/alipay/notify").permitAll()
                 .antMatchers("/metaPlayerWear/**").permitAll()
                 .antMatchers("/userHold/app/top").permitAll()
+                .antMatchers("/websocket/**").permitAll()
+                .antMatchers("/metaAdvertRecord/metaQuery").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

+ 20 - 0
src/main/java/com/izouma/nineth/service/MetaAdvertRecordService.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.domain.MetaAdvertRecord;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.MetaAdvertRecordRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class MetaAdvertRecordService {
+
+    private MetaAdvertRecordRepo metaAdvertRecordRepo;
+
+    public Page<MetaAdvertRecord> all(PageQuery pageQuery) {
+        return metaAdvertRecordRepo.findAll(JpaUtils.toSpecification(pageQuery, MetaAdvertRecord.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 76 - 0
src/main/java/com/izouma/nineth/web/MetaAdvertRecordController.java

@@ -0,0 +1,76 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaAdvertRecord;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaAdvertRecordRepo;
+import com.izouma.nineth.service.MetaAdvertRecordService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/metaAdvertRecord")
+@AllArgsConstructor
+public class MetaAdvertRecordController extends BaseController {
+    private MetaAdvertRecordService metaAdvertRecordService;
+    private MetaAdvertRecordRepo metaAdvertRecordRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaAdvertRecord save(@RequestBody MetaAdvertRecord record) {
+
+        MetaAdvertRecord metaAdvertRecord = metaAdvertRecordRepo.findByUsedAndDel(true, false);
+        if (Objects.nonNull(metaAdvertRecord) && !Objects.equals(metaAdvertRecord.getId(), record.getId())) {
+            throw new BusinessException("当前以存在使用中的广告,请先取消使用中的广告!");
+        }
+        if (record.getId() != null) {
+            MetaAdvertRecord orig = metaAdvertRecordRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return metaAdvertRecordRepo.save(orig);
+        }
+        return metaAdvertRecordRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaAdvertRecord> all(@RequestBody PageQuery pageQuery) {
+        return metaAdvertRecordService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public MetaAdvertRecord get(@PathVariable Long id) {
+        return metaAdvertRecordRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        metaAdvertRecordRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaAdvertRecord> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @GetMapping("/metaQuery")
+    public MetaRestResult<MetaAdvertRecord> queryForMeta() {
+        MetaAdvertRecord metaAdvertRecord = metaAdvertRecordRepo.findByUsedAndDel(true, false);
+        if (Objects.isNull(metaAdvertRecord)) {
+            return MetaRestResult.returnError("当前未配置广告,请先配置!");
+        }
+        return MetaRestResult.returnSuccess(metaAdvertRecord);
+    }
+}
+

+ 441 - 0
src/main/java/com/izouma/nineth/websocket/WebSocket.java

@@ -0,0 +1,441 @@
+package com.izouma.nineth.websocket;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.izouma.nineth.domain.MetaMMOLoginInfo;
+import com.izouma.nineth.dto.MMOMessage;
+import com.izouma.nineth.dto.MMOSingleMessage;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaMMOLoginInfoRepo;
+import com.izouma.nineth.utils.ApplicationContextUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Service
+@ServerEndpoint(value = "/websocket/mmo/{nickName}/{userId}")
+@Slf4j
+public class WebSocket {
+
+    /**
+     * 当前在线的客户端map
+     */
+    private static Map<String, WebSocket> clients = new ConcurrentHashMap();
+
+    /**
+     * session
+     */
+    private Session session;
+
+    /**
+     * 用户id
+     */
+    private String userId;
+
+    /**
+     * uuid
+     */
+    private String uuid;
+
+    /**
+     * key
+     */
+    private String key;
+
+    /**
+     * cityId
+     */
+    private Long cityId;
+
+    /**
+     * 区域id
+     */
+    private Long regionId;
+
+    private final RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
+
+    Session getSession() {
+        return this.session;
+    }
+
+    private MetaMMOLoginInfoRepo metaMMOLoginInfoRepo;
+
+    public void init() {
+        if (this.metaMMOLoginInfoRepo == null) {
+            metaMMOLoginInfoRepo = (MetaMMOLoginInfoRepo) ApplicationContextUtil.getBean("metaMMOLoginInfoRepo");
+        }
+    }
+
+    @OnOpen
+    public void onOpen(@PathParam("nickName") String nickName, @PathParam("userId") String userId, Session session) {
+        init();
+        try {
+            // 判断当前玩家是否在线
+            if (clients.containsKey(userId)) {
+                WebSocket webSocket = clients.get(userId);
+                // 关闭当前的链接
+                MMOSingleMessage mmoSingleMessage = new MMOSingleMessage();
+                mmoSingleMessage.setMessageType(6);
+                mmoSingleMessage.setMessage("您已在其他地方登录");
+                webSocket.sendMessageTo(JSON.toJSONString(mmoSingleMessage), userId);
+                webSocket.getSession().close();
+            }
+            // 获取上次登录的信息
+            MetaMMOLoginInfo metaMMOLoginInfo = metaMMOLoginInfoRepo.findLastByUserId(Long.parseLong(userId));
+            if (Objects.isNull(metaMMOLoginInfo)) {
+                // 首次登陆 初始化玩家信息
+                metaMMOLoginInfo = new MetaMMOLoginInfo();
+                metaMMOLoginInfo.setUserId(Long.parseLong(userId));
+                metaMMOLoginInfo.setNickname(nickName);
+                metaMMOLoginInfo.setRegionId(0L);
+                metaMMOLoginInfo.setCityId(0L);
+                metaMMOLoginInfo.setAxisX(0F);
+                metaMMOLoginInfo.setAxisY(0F);
+                metaMMOLoginInfo.setAxisZ(0F);
+                metaMMOLoginInfo.setEulerX(0F);
+                metaMMOLoginInfo.setEulerY(0F);
+                metaMMOLoginInfo.setEulerZ(0F);
+            }
+            log.info("现在来连接的sessionId:" + session.getId() + "玩家id:" + userId + "玩家昵称" + nickName);
+            this.userId = userId;
+            this.session = session;
+            // 生成缓存key 规则 --> cityId:regionId
+            this.key = String.valueOf(metaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(metaMMOLoginInfo.getRegionId()));
+            this.cityId = metaMMOLoginInfo.getCityId();
+            this.regionId = metaMMOLoginInfo.getRegionId();
+            // 玩家信息存入websocket
+            clients.put(userId, this);
+            MMOMessage mmoMessage = new MMOMessage();
+            mmoMessage.setMessageType(5);
+            mmoMessage.setMessage(metaMMOLoginInfo);
+            sendMessageTo(JSON.toJSONString(mmoMessage), userId);
+            this.uuid = UUID.randomUUID().toString();
+            // 玩家信息入库
+            MetaMMOLoginInfo newMetaMMOLoginInfo = new MetaMMOLoginInfo();
+            newMetaMMOLoginInfo.setNickname(metaMMOLoginInfo.getNickname());
+            newMetaMMOLoginInfo.setUserId(metaMMOLoginInfo.getUserId());
+            newMetaMMOLoginInfo.setCityId(metaMMOLoginInfo.getCityId());
+            newMetaMMOLoginInfo.setRegionId(metaMMOLoginInfo.getRegionId());
+            newMetaMMOLoginInfo.setAxisX(metaMMOLoginInfo.getAxisX());
+            newMetaMMOLoginInfo.setAxisY(metaMMOLoginInfo.getAxisY());
+            newMetaMMOLoginInfo.setAxisZ(metaMMOLoginInfo.getAxisZ());
+            newMetaMMOLoginInfo.setEulerX(metaMMOLoginInfo.getEulerX());
+            newMetaMMOLoginInfo.setEulerY(metaMMOLoginInfo.getEulerY());
+            newMetaMMOLoginInfo.setEulerZ(metaMMOLoginInfo.getEulerZ());
+            newMetaMMOLoginInfo.setOnLineTime(LocalDateTime.now());
+            newMetaMMOLoginInfo.setUUID(uuid);
+            metaMMOLoginInfoRepo.save(newMetaMMOLoginInfo);
+        } catch (Exception e) {
+            String errMsg = String.format("[%S]上线的时候发生了错误,错误信息[%S]", userId, e.getMessage());
+            log.info(errMsg);
+            throw new BusinessException(errMsg);
+        }
+    }
+
+    @OnError
+    public void onError(Session session, Throwable error) {
+        // 异常处理
+        log.info(String.format("服务端发生了错误:[%S]", error.getMessage()));
+    }
+
+
+    @OnClose
+    public void onClose() {
+        init();
+        // 查询地图中玩家信息
+        MetaMMOLoginInfo metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(this.userId);
+        if (Objects.isNull(metaMMOLoginInfo)) {
+            // 如果缓存中玩家信息为空,根据UUID查询数据库
+            MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findByUUID(uuid);
+            dbMetaMMOLoginInfo.setOffLineTime(LocalDateTime.now());
+            // 更新离线时间
+            metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+            clients.remove(userId);
+            return;
+        }
+        String key = String.valueOf(metaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(metaMMOLoginInfo.getRegionId()));
+        List<String> userIds = redisTemplate.opsForList().range(key, 0, -1);
+        // 分发下线消息给区域内其他玩家
+        buildMessageForSendingToAllOther(userIds, 3, metaMMOLoginInfo);
+        MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findLastByUserId(metaMMOLoginInfo.getUserId());
+        dbMetaMMOLoginInfo.setOffLineTime(LocalDateTime.now());
+        metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+        clients.remove(userId);
+        // 删除redis中自己的信息
+        redisTemplate.delete(userId);
+        // 移除地图中自己的信息
+        redisTemplate.opsForList().remove(key, 0, userId);
+    }
+
+
+    @OnMessage
+    public void onMessage(String message, Session session) {
+        init();
+        log.info("来自客户端消息:" + message + "客户端的id是:" + session.getId());
+        if (StringUtils.isBlank(message)) {
+            log.info("message is null");
+            return;
+        }
+        JSONObject jsonObject = JSON.parseObject(message);
+        if (Objects.isNull(jsonObject.getString("type"))) {
+            String errMsg = "操作类型为空";
+            log.error(errMsg);
+            throw new BusinessException(errMsg);
+        }
+        if (Objects.isNull(jsonObject.getString("cityId"))) {
+            String errMsg = "cityId为空";
+            log.error(errMsg);
+            throw new BusinessException(errMsg);
+        }
+        if (Objects.isNull(jsonObject.getString("regionId"))) {
+            String errMsg = "regionId为空";
+            log.error(errMsg);
+            throw new BusinessException(errMsg);
+        }
+        int type = Integer.parseInt(jsonObject.getString("type"));
+        String cityId = jsonObject.getString("cityId");
+        String regionId = jsonObject.getString("regionId");
+        String key = cityId.concat(":").concat(regionId);
+        // 从当前地图中获取其他玩家信息
+        List<String> userIds = redisTemplate.opsForList().range(key, 0, -1);
+        // 进入地图 把自己位置信息通知别人,获取其他人的位置信息
+        if (type == 1) {
+            MetaMMOLoginInfo metaMMOLoginInfo = buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId));
+            // 判断地图中是否存在玩家
+            if (CollectionUtils.isNotEmpty(userIds)) {
+                List<MetaMMOLoginInfo> metaMMOLoginInfos = redisTemplate.opsForValue().multiGet(userIds);
+                if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
+                    // 过滤自己
+                    metaMMOLoginInfos = remove(metaMMOLoginInfos);
+                    if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
+                        // 分发区域内其他玩家信息给自己
+                        buildMessageForSendingToUser(userId, 1, metaMMOLoginInfos);
+                        // 分发自己信息给区域内其他玩家
+                        buildMessageForSendingToAllOther(userIds, 4, metaMMOLoginInfo);
+                    }
+                }
+            }
+            // 将自己信息存到redis中
+            redisTemplate.opsForValue().set(userId, metaMMOLoginInfo);
+            redisTemplate.opsForList().leftPush(key, userId);
+        }
+        // 如果是区域直接切换区域
+        if (type == 2) {
+            MetaMMOLoginInfo metaMMOLoginInfo = buildMetaMMOLoginInfo(jsonObject, Long.parseLong(cityId), Long.parseLong(regionId));
+            if (CollectionUtils.isNotEmpty(userIds)) {
+                // 分发玩家信息给区域内其他玩家
+                buildMessageForSendingToAllOther(userIds, 4, metaMMOLoginInfo);
+                // 分发区域内其他玩家信息给自己
+                List<MetaMMOLoginInfo> metaMMOLoginInfos = redisTemplate.opsForValue().multiGet(userIds);
+                if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
+                    // 过滤自己
+                    metaMMOLoginInfos = remove(metaMMOLoginInfos);
+                    if (CollectionUtils.isNotEmpty(metaMMOLoginInfos)) {
+                        buildMessageForSendingToUser(userId, 1, metaMMOLoginInfos);
+                    }
+                }
+            }
+            // 分发玩家信息给之前区域内其他玩家
+            MetaMMOLoginInfo oldMetaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(userId);
+            if (Objects.isNull(oldMetaMMOLoginInfo)) {
+                String errMsg = "缺失玩家上次区域的地图缓存信息";
+                log.error(errMsg);
+                throw new BusinessException(errMsg);
+            }
+            String oldKey = String.valueOf(oldMetaMMOLoginInfo.getCityId()).concat(":").concat(String.valueOf(oldMetaMMOLoginInfo.getRegionId()));
+            List<String> oldUserIds = redisTemplate.opsForList().range(oldKey, 0, -1);
+            if (CollectionUtils.isEmpty(oldUserIds)) {
+                String errMsg = "查询不到上次所进入的区域的玩家信息";
+                log.error(errMsg);
+                throw new BusinessException(errMsg);
+            }
+            // 分发消息给之前区域的玩家
+            buildMessageForSendingToAllOther(oldUserIds, 3, oldMetaMMOLoginInfo);
+            // 清除玩家上次缓存的地图信息
+            redisTemplate.opsForList().remove(oldKey, 0, userId);
+            // 缓存玩家新的地图信息
+            redisTemplate.opsForValue().set(userId, metaMMOLoginInfo);
+            redisTemplate.opsForList().leftPush(key, userId);
+        }
+        // 玩家在地图内移动
+        if (type == 3) {
+            MetaMMOLoginInfo metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(userId);
+            if (Objects.isNull(metaMMOLoginInfo)) {
+                String errMsg = "缓存中不存在本玩家信息";
+                log.error(errMsg);
+                throw new BusinessException(errMsg);
+            }
+            // 分发玩家信息给区域内其他玩家
+            buildMessageForSendingToAllOther(userIds, 2, metaMMOLoginInfo);
+            // 更新缓存中玩家位置信息
+            buildMetaMMOLoginInfo(metaMMOLoginInfo, jsonObject);
+            redisTemplate.opsForValue().set(userId, metaMMOLoginInfo);
+        }
+        // 进入大厅
+        if (type == 4) {
+            MetaMMOLoginInfo metaMMOLoginInfo = (MetaMMOLoginInfo) redisTemplate.opsForValue().get(userId);
+            if (Objects.isNull(metaMMOLoginInfo)) {
+                String errMsg = "缓存中不存在本玩家信息";
+                log.error(errMsg);
+                throw new BusinessException(errMsg);
+            }
+            // 分发玩家信息给区域内其他玩家
+            buildMessageForSendingToAllOther(userIds, 3, metaMMOLoginInfo);
+            // 更新库中玩家位置信息
+            MetaMMOLoginInfo dbMetaMMOLoginInfo = metaMMOLoginInfoRepo.findByUUID(uuid);
+            dbMetaMMOLoginInfo.setAxisX(metaMMOLoginInfo.getAxisX());
+            dbMetaMMOLoginInfo.setAxisY(metaMMOLoginInfo.getAxisY());
+            dbMetaMMOLoginInfo.setAxisZ(metaMMOLoginInfo.getAxisZ());
+            dbMetaMMOLoginInfo.setEulerX(metaMMOLoginInfo.getEulerX());
+            dbMetaMMOLoginInfo.setEulerY(metaMMOLoginInfo.getEulerY());
+            dbMetaMMOLoginInfo.setEulerZ(metaMMOLoginInfo.getEulerZ());
+            dbMetaMMOLoginInfo.setCityId(metaMMOLoginInfo.getCityId());
+            dbMetaMMOLoginInfo.setRegionId(metaMMOLoginInfo.getRegionId());
+            metaMMOLoginInfoRepo.save(dbMetaMMOLoginInfo);
+        }
+    }
+
+    /**
+     * 分发玩家信息给区域内其他玩家
+     *
+     * @param userIds          玩家id集合
+     * @param messageType      消息类型
+     * @param metaMMOLoginInfo 消息体
+     */
+    private void buildMessageForSendingToAllOther(List<String> userIds, int messageType, MetaMMOLoginInfo metaMMOLoginInfo) {
+        MMOMessage mmoMessage = new MMOMessage();
+        mmoMessage.setMessageType(messageType);
+        mmoMessage.setMessage(metaMMOLoginInfo);
+        sendMessageAllOther(userIds, JSON.toJSONString(mmoMessage));
+    }
+
+    /**
+     * 分发区域内其他玩家信息给自己
+     *
+     * @param userId            玩家id
+     * @param messageType       消息类型
+     * @param metaMMOLoginInfos 消息体(其他玩家信息)
+     */
+    private void buildMessageForSendingToUser(String userId, int messageType, List<MetaMMOLoginInfo> metaMMOLoginInfos) {
+        MMOMessage mmoMessage = new MMOMessage();
+        mmoMessage.setMessageType(messageType);
+        mmoMessage.setMap(metaMMOLoginInfos);
+        sendMessageTo(JSON.toJSONString(mmoMessage), userId);
+    }
+
+    /**
+     * 移除自己信息
+     *
+     * @param metaMMOLoginInfos 信息集合
+     * @return 非自己信息集合
+     */
+    private List<MetaMMOLoginInfo> remove(List<MetaMMOLoginInfo> metaMMOLoginInfos) {
+        return metaMMOLoginInfos.stream()
+                .filter(mmoLoginInfo -> !Objects.equals(mmoLoginInfo.getUserId(), userId)).collect(Collectors.toList());
+    }
+
+    /**
+     * 给指定用户分发消息
+     *
+     * @param message  消息体
+     * @param toUserId 用户id
+     */
+    public void sendMessageTo(String message, String toUserId) {
+        clients.values().forEach(webSocket -> {
+            if (webSocket.userId.equals(toUserId)) {
+                log.info(String.format("给指定的在线用户发送消息,当前在线人员为[%S]。消息:[%S]", webSocket.userId.toString(), message));
+                try {
+                    webSocket.session.getBasicRemote().sendText(message);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+
+    /**
+     * 分发下线消息给当前城市当前区域中其他玩家
+     *
+     * @param userIds 其他玩家id集合
+     * @param message 消息体
+     */
+    public void sendMessageAllOther(List<String> userIds, String message) {
+        clients.values().forEach(webSocket -> {
+            if (!Objects.equals(webSocket.userId, userId) && userIds.contains(webSocket.userId)) {
+                log.info(String.format("服务器给所有当前区域内在线用户发送消息,当前在线人员为[%S]。消息:[%S]", webSocket.userId.toString(), message));
+                try {
+                    webSocket.session.getBasicRemote().sendText(message);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 分发消息给所有玩家
+     *
+     * @param message 消息体
+     */
+    public void sendMessageAll(String message) {
+        clients.values().forEach(webSocket -> {
+            log.info(String.format("服务器给所有当前区域内在线用户发送消息,当前在线人员为[%S]。消息:[%S]", webSocket.userId.toString(), message));
+            try {
+                webSocket.session.getBasicRemote().sendText(message);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    private MetaMMOLoginInfo buildMetaMMOLoginInfo(JSONObject jsonObject, Long cityId, Long regionId) {
+        // 拿到自己进入地图市时的信息通知给其他人,获取当前区域内其他玩家信息
+        String role = jsonObject.getString("role");
+        // 获取到进入地图时自己的信息
+        MetaMMOLoginInfo metaMMOLoginInfo = new MetaMMOLoginInfo();
+        buildMetaMMOLoginInfo(metaMMOLoginInfo, jsonObject);
+        metaMMOLoginInfo.setUserId(Long.parseLong(userId));
+        metaMMOLoginInfo.setCityId(cityId);
+        metaMMOLoginInfo.setRegionId(regionId);
+        if (StringUtils.isNotBlank(role)) {
+            metaMMOLoginInfo.setRole(role);
+        }
+        return metaMMOLoginInfo;
+    }
+
+    private void buildMetaMMOLoginInfo(MetaMMOLoginInfo metaMMOLoginInfo, JSONObject jsonObject) {
+        if (Objects.nonNull(jsonObject.getString("axisX"))) {
+            metaMMOLoginInfo.setAxisX(Float.parseFloat(jsonObject.getString("axisX")));
+        }
+        if (Objects.nonNull(jsonObject.getString("axisY"))) {
+            metaMMOLoginInfo.setAxisY(Float.parseFloat(jsonObject.getString("axisY")));
+        }
+        if (Objects.nonNull(jsonObject.getString("axisZ"))) {
+            metaMMOLoginInfo.setAxisZ(Float.parseFloat(jsonObject.getString("axisZ")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerX"))) {
+            metaMMOLoginInfo.setEulerX(Float.parseFloat(jsonObject.getString("eulerX")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerY"))) {
+            metaMMOLoginInfo.setEulerY(Float.parseFloat(jsonObject.getString("eulerY")));
+        }
+        if (Objects.nonNull(jsonObject.getString("eulerZ"))) {
+            metaMMOLoginInfo.setEulerZ(Float.parseFloat(jsonObject.getString("eulerZ")));
+        }
+        if (Objects.nonNull(jsonObject.getString("nickName"))) {
+            metaMMOLoginInfo.setNickname(jsonObject.getString("nickName"));
+        }
+    }
+}

+ 1 - 0
src/main/resources/genjson/MetaAdvertRecord.json

@@ -0,0 +1 @@
+{"tableName":"MetaAdvertRecord","className":"MetaAdvertRecord","remark":"元宇宙广告","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/java/com/izouma/nineth","viewPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src/views","routerPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src","resourcesPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"pic","modelName":"pic","remark":"图片","showInList":true,"showInForm":true,"formType":"singleImage","required":true},{"name":"link","modelName":"link","remark":"地址","showInList":true,"showInForm":true,"formType":"singleLineText","required":true}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.MetaAdvertRecord"}

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 15434
src/main/vue/package-lock.json


+ 16 - 0
src/main/vue/src/router.js

@@ -1035,6 +1035,22 @@ const router = new Router({
                    meta: {
                       title: '元宇宙彩蛋',
                    },
+               },
+                {
+                    path: '/metaAdvertRecordEdit',
+                    name: 'MetaAdvertRecordEdit',
+                    component: () => import(/* webpackChunkName: "metaAdvertRecordEdit" */ '@/views/MetaAdvertRecordEdit.vue'),
+                    meta: {
+                       title: '元宇宙广告编辑',
+                    },
+                },
+                {
+                    path: '/metaAdvertRecordList',
+                    name: 'MetaAdvertRecordList',
+                    component: () => import(/* webpackChunkName: "metaAdvertRecordList" */ '@/views/MetaAdvertRecordList.vue'),
+                    meta: {
+                       title: '元宇宙广告',
+                    },
                }
                 /**INSERT_LOCATION**/
             ]

+ 120 - 0
src/main/vue/src/views/MetaAdvertRecordEdit.vue

@@ -0,0 +1,120 @@
+<template>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                删除
+            </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form :model="formData" :rules="rules" ref="form" label-width="82px" label-position="right"
+                    size="small" style="max-width: 500px;">
+                    <el-form-item prop="pic" label="图片">
+                        <single-upload v-model="formData.pic"></single-upload>
+                    </el-form-item>
+                    <el-form-item prop="link" label="地址">
+                        <el-input v-model="formData.link"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="used" label="是否使用">
+						<el-switch v-model="formData.used"> </el-switch>
+					</el-form-item>
+                    <el-form-item class="form-submit">
+                        <el-button @click="onSave" :loading="saving" type="primary">
+                            保存
+                        </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button>
+                        <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name: 'MetaAdvertRecordEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('metaAdvertRecord/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {
+            },
+            rules: {
+                pic: [
+                    {
+                        required: true,
+                        message: '请输入图片',
+                        trigger: 'blur'
+                    },
+                ],
+                link: [
+                    {
+                        required: true,
+                        message: '请输入地址',
+                        trigger: 'blur'
+                    },
+                ],
+            },
+        }
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/metaAdvertRecord/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$confirm('删除将无法恢复,确认要删除么?', '警告', { type: 'error' }).then(() => {
+                return this.$http.post(`/metaAdvertRecord/del/${this.formData.id}`)
+            }).then(() => {
+                this.$message.success('删除成功');
+                this.$router.go(-1);
+            }).catch(e => {
+                if (e !== 'cancel') {
+                    console.log(e);
+                    this.$message.error((e || {}).error || '删除失败');
+                }
+            })
+        },
+    }
+}
+</script>
+<style lang="less" scoped>
+</style>

+ 153 - 0
src/main/vue/src/views/MetaAdvertRecordList.vue

@@ -0,0 +1,153 @@
+<template>
+    <div class="list-view">
+        <page-title>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" :disabled="fetchingData || downloading"
+                class="filter-item">
+                新增
+            </el-button>
+        </page-title>
+        <el-table :data="tableData" row-key="id" ref="table" header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell" row-class-name="table-row" cell-class-name="table-cell"
+            :height="tableHeight" v-loading="fetchingData">
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50">
+            </el-table-column>
+            <el-table-column prop="id" label="ID" width="100">
+            </el-table-column>
+            <el-table-column prop="createdAt" label="时间">
+            </el-table-column>
+            <el-table-column prop="pic" label="图片">
+                <template slot-scope="{row}">
+                    <el-image style="width: 30px; height: 30px" :src="row.pic" fit="cover"
+                        :preview-src-list="[row.pic]"></el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="link" label="链接地址">
+            </el-table-column>
+            <el-table-column prop="used" label="是否使用">
+				<template slot-scope="{ row }">
+					<el-tag :type="row.used ? '' : 'info'"> {{ row.used }} </el-tag>
+				</template>
+			</el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="150">
+                <template slot-scope="{row}">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination background @size-change="onSizeChange" @current-change="onCurrentChange" :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]" :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper" :total="totalElements">
+            </el-pagination>
+        </div>
+
+    </div>
+</template>
+<script>
+import { mapState } from "vuex";
+import pageableTable from "@/mixins/pageableTable";
+
+export default {
+    name: 'MetaAdvertRecordList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: "",
+            url: "/metaAdvertRecord/all",
+            downloading: false,
+        }
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            return { search: this.search, query: { del: false } };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: "/metaAdvertRecordEdit",
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: "/metaAdvertRecordEdit",
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get("/metaAdvertRecord/excel", {
+                    responseType: "blob",
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement("a");
+                    link.href = downloadUrl;
+                    link.setAttribute(
+                        "download",
+                        res.headers["content-disposition"].split("filename=")[1]
+                    );
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' }).then(() => {
+                return this.$http.post(`/metaAdvertRecord/del/${row.id}`)
+            }).then(() => {
+                this.$message.success('删除成功');
+                this.getData();
+            }).catch(e => {
+                if (e !== 'cancel') {
+                    this.$message.error(e.error);
+                }
+            })
+        },
+    }
+}
+</script>
+<style lang="less" scoped>
+</style>

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff