目录
前端:
- android
后端:
- springboot
- springsecurity
- mybatis-plus
- redis
- websocket
项目部署阿里云服务器
主要介绍引用的第三方框架、技术点

不论是目录结构,还是代码,都应注意分层

viewBinding
- android {
- buildFeatures {
- viewBinding true
- }
- }

xutils
引入依赖
- implementation 'org.xutils:xutils:3.8.5'
- // gson
- implementation 'com.google.code.gson:gson:2.8.2'
AssNineGridView

badgeview

lottie


服务器 + websocket

单文件、多文件

走马灯
- <TextView
- android:id="@+id/zm_tv"
- android:layout_width="260dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="32dp"
- android:ellipsize="marquee"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:marqueeRepeatLimit="-1"
- android:padding="10dp"
- android:singleLine="true"
- android:text="@string/trotting_horse_lamp"
- android:textColor="@color/baby_blue"
- android:textSize="20dp"
- app:layout_constraintHorizontal_bias="0.497"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toTopOf="parent">
- <requestFocus />
- TextView>
输入提示属性
android:hint="请输入用户名"
文本改变监听器
- searchEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
- @Override
- public void afterTextChanged(Editable editable) {
- // 文本改变后执行的动作
- }
- });
webView
spinner

使用场景:比如用户有多个字段,但是当我们注册,登录时,并不需要这么多字段。
好处:节省数据交互时空间的消耗,有提升效率的作用。

实际上,我们需要的字段是比较少的,比如:

在UserController层:

不过在最后,我们存入数据库的肯定还是原来的User,而不是UserRegisterVM, 所以,我们要对这两者之间作个转换:
User user = modelMapper.map(model, User.class);
以下是对modelMapper的封装,可以当做API调用。
- public class BaseApiController {
- /**
- * The constant DEFAULT_PAGE_SIZE.
- */
- protected final static String DEFAULT_PAGE_SIZE = "10";
- /**
- * The constant modelMapper.
- */
- protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();
- }
需要使用modelMapper的时候,要继承BaseApiController 这里对应的是一个单例modelMapper
- public class ModelMapperSingle {
- /**
- * The constant modelMapper.
- */
- protected final static ModelMapper modelMapper = new ModelMapper();
- private final static ModelMapperSingle modelMapperSingle = new ModelMapperSingle();
-
- static {
- modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
- modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
- }
-
- /**
- * Instance model mapper.
- *
- * @return the model mapper
- */
- public static ModelMapper Instance() {
- return modelMapperSingle.modelMapper;
- }
- }

- /*
- * @description: 将文件保存在本地
- * @author: xingxg
- * @date: 2022/11/8 17:32
- * @param: file , 与前端的名字相对应
- * @return: 返回文件保存的路径
- **/
-
- @PostMapping("/upload")
- public CommonResult
> fileLoad(MultipartFile[] file, HttpServletRequest request) throws IOException {
- String saveLocation = "e:/images/";
- //String saveLocation = "/images/";
- String fileSaveName = "";
- List
imageUri = new ArrayList<>(); - for (MultipartFile multipartFile : file) {
- fileSaveName = UUID.randomUUID().toString() + multipartFile.getOriginalFilename();
- multipartFile.transferTo(new File(saveLocation, fileSaveName));
- String result = request.getScheme() + "://" +
- request.getServerName() + ":" +
- request.getServerPort() + "/" +
- fileSaveName;
- imageUri.add(result);
- }
- return CommonResult.ok(imageUri);
- }
此前的状态是,两个人进行聊天,A发一句,B需要重新进入聊天界面才能看到A新发的消息,这是不合理的,针对该问题进行改进。
预期效果,不用重新进入页面,只要由新消息传进来,就可以展示新的数据。
解决方法1:
前端周期性地去请求后端数据,达到实时聊天的效果。
这种方式非常消耗资源,明显是不合理的。
解决方法2:
A向B发消息, A将消息推送到服务器上,由服务器主动推动消息给B
android
引入依赖
implementation "org.java-websocket:Java-WebSocket:1.3.7"
android核心代码
- public void addUserToService() {
- URI serverURI = URI.create("ws://192.168.130.1:9000/chat/" + Global.username);
-
- webSocketClient = new WebSocketClient(serverURI) {
- @Override
- public void onOpen(ServerHandshake handshakedata) {
- Log.i("WebSocketClient", "onOpen");
- }
- @Override
- public void onMessage(String message) {
- loadMessage(); // 有新消息传给本用户的话,重新请求后端,加载数据。
- }
- @Override
- public void onClose(int code, String reason, boolean remote) {
- Log.i("WebSocketClient", "onClose");
- }
- @Override
- public void onError(Exception ex) {
- Log.i("WebSocketClient", "onError");
- }
- };
- try {
- webSocketClient.connectBlocking();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
后端核心代码
- package com.huiliyi.backend.utils;
-
- import com.fasterxml.jackson.databind.ObjectMapper;
- import lombok.Data;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Component;
-
- import javax.websocket.*;
- import javax.websocket.server.PathParam;
- import javax.websocket.server.ServerEndpoint;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
-
- @Slf4j
- @ServerEndpoint(value = "/chat/{username}")
- @Component
- @Data
- public class ChatEndpoint {
-
- //用来存储每个用户客户端对象的ChatEndpoint对象
- public static Map
onlineUsers = new ConcurrentHashMap<>(); -
- //声明session对象,通过对象可以发送消息给指定的用户
- private Session session;
-
- private String username;
- //连接建立
- @OnOpen
- public void onOpen(@PathParam("username") String username,
- Session session, EndpointConfig config){
- this.session = session;
- this.username = username;
- //存储登陆的对象
- onlineUsers.put(username,this);
- log.info("onOpen:{}", username);
- }
- //收到消息
- @OnMessage
- public void onMessage(String toName,Session session){
- //将数据转换成对象
- try {
- //发送数据
- log.info("onMessage:{}",toName);
- onlineUsers.get(toName).session.getBasicRemote().sendText("来消息了");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- //关闭
- @OnClose
- public void onClose(Session session) {
- //从容器中删除指定的用户
- log.info("onClose:{}", username);
- onlineUsers.remove(username);
- }
- }
public static Map onlineUsers = new ConcurrentHashMap<>();
系统中用户名昵称是全局唯一的,所以可以将用户名作为主键,使得每个用户对应一个ChatEndpoint
采用宝塔面板进行傻瓜式操作。
注意在阿里云的云服务器实例开放所需要的所有端口
在linux本地可以登录mysql, 但是本地机连不上远程的?

原因是远程主机没有开放3306端口
开放3306端口号后,还是出现问题:

这是因为远程的 主机限制了只能localhost连接数据库
所以,在远程主机上修改这个限制,就可以解决这个问题了
1、在安装Mysql数据库的主机上登录root用户:
mysql -u root -p2、执行命令:
use mysql; select host from user where user='root';可以得到结果:
表名此时mysql数据库只允许localhost连接,所以此前才会拒绝本地的主机连接远程服务器。
3、将Host设置为通配符%
update user set host = '%' where user ='root';4、Host修改完成后记得执行flush privileges使配置立即生效
flush privileges;
使用Apipost

在课程设计的过程中,项目采用前后端分离技术。
有的人负责编写后端,有的人负责编写前端。这可以使得整个项目的结构更加清晰明了,但是前端如何去与后端交互是一个问题。
因为前后端是不同的人写的,所以需要提前规范好API文档。
而ApiPost不仅可以对API接口进行调试,还可以自动生成相对应的文档,是一个非常实用的开发的工具。

生成的文档,就比较清晰,比自己写一个API接口文档要规范的多

renren.io
人人开源是Gitee上的一个开源项目,可以快速搭建后台管理系统。

1、 创建一个总目录,用来承载多个项目

2、初始化为git仓库
进入test_project



同理,再拉取2个项目


最终的目录层次为:





运行项目, 访问Swagger文档路径,如果可以正常访问,则表明一切正常。


打开renren-fast-vue项目, 这里本人使用的是WebStorm编译器

依赖安装完成, 打开终端,运行项目
这里的命令行是:
npm run dev


登录成功后:

此时,这个项目还算是一个空壳,没有管理到真正自身的项目
这是就需要另一个项目, renren-generator 实现代码生成!
代码生成需要提供关于数据库的表


修改generator.properties

注释出现乱码问题,修改字符集编码


在renren_fast数据库中插入你真正项目中所使用的表, 可以在已有的数据库中选择转储sql文件,然后在renren_fast中执行这个sql文件即可。

然后,就可以在代码生成器中查到你刚刚插入的数据库表。
运行项目,访问localhost


代码就自动生成了。
解压文件,可以看到有如下内容:

这些sql文件,直接拖到navicat里执行就可以了。
作用是用来生成管理的子菜单


将后端代码复制到renren-fast项目下的 module

最后,就可以实现对模块的管理。

人人开源后端中,自带了一个User,而我们自身模块有一个名字一模一样的User
冲突1:
Spring容器中UserService组件不唯一
解决:
冲突2:
UserDao也是自动注入的。虽然说对应的mapper文件可以映射过去,但是名字重复了,还是会有点问题.
解决方法:
冲突3:
ShiroConfig 已经存在对UserEntity的映射。意思就是说,类名不能重复。
解决方法:修改类名

可以看到,用户的ID后几位都为0,这是由于前段接收Long类型时,出现精度丢失,以下为解决方式:

