Java消息系統簡單設計與實現

来源:https://www.cnblogs.com/wmyskxz/archive/2019/01/07/10235319.html
-Advertisement-
Play Games

前言: 由於導師在我的畢設項目裡加了消息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單... 需求分析 我的畢設是一個博客系統 ,類似於簡書這樣的,所以消息系統也類似,在用戶的消息里包含了有:喜歡和贊、評論、關註、私信這樣的一類東西,這樣的一個系統應該包含以下的功能: 1. ...


前言:由於導師在我的畢設項目裡加了消息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單...

需求分析

我的畢設是一個博客系統,類似於簡書這樣的,所以消息系統也類似,在用戶的消息里包含了有:喜歡和贊、評論、關註、私信這樣的一類東西,這樣的一個系統應該包含以下的功能:

    1. 當用戶評論/關註/點贊時能夠通知到被評論/關註/點贊的用戶,並生成像如下格式的提示信息(允許取消關註/點贊但不收到通知):

    我沒有 關註了 你
    三顆 喜歡了你的文章 《Java消息系統簡單設計與實現》
    心臟 評論了你的文章 《Java消息系統簡單設計與實現》

    1. 用戶之間能夠發送/接受私信,不需要像QQ那樣建立長連接實現實時通信,但刷新列表能看到新消息,並且界面類似QQ聊天界面一左一右,允許刪除私信
    1. 管理員能發送通告,其實就像是用管理員的賬號給每一個用戶發送私信;
    1. 可以查看關註的用戶最新發表的文章,得到類似推送的效果;
    1. 所有消息當然也要標註好消息已讀or未讀,登錄就能得到消息提醒標識好有多少未讀消息,像是QQ消息右上角的小紅點那樣類似;

OK,大致就是以上的功能,那麼問題來了:這要怎麼設計啊?

進一步分析

其實可以根據上面的需求分析,把上面的消息大致可以分為公告(Announcement)、提醒(Remind)、私信(Message)三類,我們可以大致抽象出一個 通知(Notify) 模型:

發送者 接受者 信息類型 動作類型 通知內容 是否已讀 消息創建時間
粉絲1號 我沒有三顆心臟 提醒 關註 粉絲1號 關註了 你 xx:xx:xx
粉絲1號 我沒有三顆心臟 提醒 喜歡和贊 粉絲1號 喜歡了你的文章 《Java消息系統簡單設計與實現》 xx:xx:xx
粉絲1號 我沒有三顆心臟 提醒 評論 粉絲1號 評論了你的文章 《Java消息系統簡單設計與實現》 xx:xx:xx
粉絲2號 我沒有三顆心臟 私信 你收到了來自 粉絲2號 的 1 條私信 xx:xx:xx

上面加了一些數據以便理解,不過話說粉絲1號果然是真愛粉,又關註又喜歡又評論,嘻嘻嘻嘻...

emm.這樣的模型能夠勝任我們的工作嗎?我也不知道..不過根據這個模型能夠想出大概的這樣的創建通知的邏輯:

似乎看上去也沒有什麼大問題..不過既然消息內容都可以根據動作類型自動生成的了,加上私信和公告的內容因為長度問題也肯定不保存在這張表裡的好,所以我們在設計資料庫時乾脆把通知內容這條去掉不要,當信息類型是公告或者私信時可以根據這條通知的 id 在相應的表中找到相應的數據就可以了,emm..我覺得可以

順下去想想其實腦中有了一個大概,這樣的模型還容易設計和想到,其實主要的問題還是下麵的那些

問題一:單表數據大了怎麼辦?

如果當用戶量上去到一定量的時候,那麼這張 通知表 勢必會變得巨大,因為不管是我們的公告、提醒還是私信都會在這個通知表上創建一條數據,到時候就會面臨查詢慢的問題,問題的答案是:我也不知道..

所以我們的規定是:不考慮像簡書這樣超大用戶量,能夠應付畢設就好啦..簡單設計,嘻嘻嘻..不過也不要太不相信MySQL的性能,還是有一定容納能力的!

問題二:用戶要怎樣正確得到自己的未讀消息呢?

暴力一點方法是,反正通知表裡有用戶所有的消息,直接讀取完,然後通過是否已讀欄位就能夠找到正確的所有未讀消息了,這..這麼簡單嗎?

其實有思考過使用時間或者另建一張保存有最新已讀到哪條消息的表,但用戶可以選擇有一些讀有一些不讀,這兩個似乎都很難達到目的...還是暴力吧

問題三:私信消息該怎麼設計?

發送者 接受者 內容 發送時間
粉絲1號 我沒有三顆心臟 我是你的真愛粉啊!我要給你生猴子! 2019年1月7日11:34:23
我沒有三顆心臟 粉絲1號 已閱...下一個... 2019年1月7日11:34:53

就像 QQ消息 一樣嘛,包含一個內容、時間、發送者和接受者,然後前端直接根據時間或者 id 排序生成一左一右的消息對話框,不過比較特殊的一點就是私信是一個雙向交流的過程,在一個對話框中我可能既是接受者也是發送者,這也無所謂嘛,稍微分析分析場景:

  • 讀取私信列表時:按照接受者和發送者一起查詢的原則,也就是查詢接受者是自己和發送者是自己的數據,然後根據時間和已讀未讀來建立私信列表;
  • 讀取私信時:這時已經有了明確的接受者和發送者,那就查詢所有 發送者是對方接受者是自己 Or 發送者是自己接受者是對方 的數據,然後在前端拼湊出一左一右的聊天框;
  • 發送私信時:先查詢之前是否有記錄,然後同上建立聊天框,點擊發送之後把發送方設為自己接收方設為私信對象,然後在通知表中新建一條未讀數據通知私信對象有私信來了;

這完全能滿足要求,只不過感覺查詢多了些..

資料庫設計

簡單弄了弄弄..看著挺難受的,不過能簡單實現功能,並且為了演示,這裡是做了一張user_follow表,表示用戶之間的關聯關係,點贊和評論與這個類似,就不多弄了..下麵給一下建表語句吧:

user表:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `username` varchar(50) NOT NULL COMMENT '用戶姓名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

user_follow表:

CREATE TABLE `user_follow` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `uid` bigint(20) NOT NULL COMMENT '用戶ID',
  `follow_uid` bigint(20) NOT NULL COMMENT '關註的用戶id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶關註表,記錄了所有用戶的關註信息';

notify表:

CREATE TABLE `notify` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `sender_id` bigint(20) NOT NULL COMMENT '發送者用戶ID',
  `reciver_id` bigint(20) NOT NULL COMMENT '接受者用戶ID',
  `type` varchar(50) NOT NULL COMMENT '消息類型:announcement公告/remind提醒/message私信',
  `is_read` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已讀,0未讀,1已讀',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間:按當前時間自動創建',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶通知表,包含了所有用戶的消息';

message表:

CREATE TABLE `message` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `notify_id` bigint(20) NOT NULL COMMENT '對應通知消息的id',
  `sender_id` bigint(20) NOT NULL COMMENT '發送者用戶ID',
  `reciver_id` bigint(20) NOT NULL COMMENT '接受者用戶ID',
  `content` varchar(1000) NOT NULL COMMENT '消息內容,最長長度不允許超過1000',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間:按當前時間自動創建',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='私信信息表,包含了所有用戶的私信信息';

根據《Java開發手冊》5.3 第六條 沒有使用任何級聯和外鍵,bingo!

Spring Boot + MyBatis 實例

第一步:基礎環境搭建

SpringBoot項目怎麼搭就不說了吧,給一給幾個關鍵的配置文件:

pom包依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- SpringBoot - MyBatis 逆向工程 -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>5.1.18</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

這裡有一個巨坑,耗費了我好半天的時間,不知道為什麼我明明引入的 5.1.18 版本的 mysql-connector-java,可 Maven 就是非要給我比較新版本的 8.0.13,這導致了在我使用 MyBatis 逆向工程生成 domain 和 mapper 的過程中出現了以下的問題:

  • 1、提示我資料庫連接的驅動名稱需要改成com.mysql.cj.jdbc.Driver而不是之前的com.mysql.jdbc.Driver,不然就報錯:

Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

  • 2、還需要設置 mysql 的時區,也就是需要將connectionURL屬性寫成"jdbc:mysql://localhost:3306/test?serverTimezone=UTC"。如果不指定serverTimezone=UTC(還必須大寫),將報錯:

java.sql.SQLException: The server time zone value '?й???????' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
  at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:695)
   at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:663)

  • 3、逆向工程會去找 MySQL 其他庫的相同表名的表,然後生成一堆亂七八糟的東西,還由於找不到主鍵 id 生成了只含 inser() 方法而不含刪除、更新方法的 Mapper 文件;

解決方法就只有自己手動去調低 mysql-connector-java 的版本到 5.xx,還找到一個跟我情況類似:https://blog.csdn.net/angel_xiaa/article/details/52474022

application.properties:

## 資料庫連接配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/message_system?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
## MyBatis相關配置
mybatis.type-aliases-package=com.wmyskxz.demo.messagesystem.domain
mybatis.mapper-locations=classpath:mapper/*.xml

在啟動類上加上註解:

@EnableTransactionManagement  // 啟註解事務管理,等同於xml配置方式的 <tx:annotation-driven />
@MapperScan("com.wmyskxz.demo.messagesystem.dao")
@SpringBootApplication
public class MessageSystemApplication {
        ....
}

第二步:MyBatis 逆向工程

新建【util】包,在下麵新建兩個類:

MybatisGenerator類:

public class MybatisGenerator {

    public static void main(String[] args) throws Exception {
        String today = "2019-1-7";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date now = sdf.parse(today);
        Date d = new Date();

        if (d.getTime() > now.getTime() + 1000 * 60 * 60 * 24) {
            System.err.println("——————未成成功運行——————");
            System.err.println("——————未成成功運行——————");
            System.err.println("本程式具有破壞作用,應該只運行一次,如果必須要再運行,需要修改today變數為今天,如:" + sdf.format(new Date()));
            return;
        }

        if (false)
            return;
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        InputStream is = MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(is);
        is.close();
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);

        System.out.println("生成代碼成功,只能執行一次,以後執行會覆蓋掉mapper,pojo,xml 等文件上做的修改");

    }
}

OverIsMergeablePlugin類:

/**
 * 解決 MyBatis 逆向工程重覆生成覆蓋問題的工具類
 */
public class OverIsMergeablePlugin extends PluginAdapter {
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        try {
            Field field = sqlMap.getClass().getDeclaredField("isMergeable");
            field.setAccessible(true);
            field.setBoolean(sqlMap, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

在【resrouces】資源文件下新建逆向工程配置文件【generatorConfig.xml】:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">

        <!--避免生成重覆代碼的插件-->
        <plugin type="com.wmyskxz.demo.messagesystem.util.OverIsMergeablePlugin"/>

        <!-- 是否去除自動生成的代碼中的註釋 true:是 false:否-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--資料庫鏈接地址賬號密碼-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/message_system?serverTimezone=UTC"
                        userId="root" password="123456">
        </jdbcConnection>
        <!-- 預設 false,把 JDBC DECIMAL 和 NUMERIC 類型解析為 Integer
             為 true 時解析為 java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!--生成pojo類存放位置-->
        <javaModelGenerator targetPackage="com.wmyskxz.demo.messagesystem.domain" targetProject="src/main/java">
            <!-- enableSubPackages:是否讓 schema 作為包的尾碼-->
            <property name="enableSubPackages" value="true"/>
            <!-- trimStrings:從資料庫返回的值被清理前後的空格 -->
            <property name="trimStrings" value="true"/>
            <!-- 是否對model添加 構造函數 -->
            <property name="constructorBased" value="true"/>
        </javaModelGenerator>
        <!--生成xml映射文件存放位置-->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--生成mapper類存放位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.wmyskxz.demo.messagesystem.dao"
                             targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!--生成對應表及類名
            tableName:要生成的表名
            domainObjectName:生成後的實例名
            enableCountByExample:Count語句中加入where條件查詢,預設為true開啟
            enableUpdateByExample:Update語句中加入where條件查詢,預設為true開啟
            enableDeleteByExample:Delete語句中加入where條件查詢,預設為true開啟
            enableSelectByExample:Select多條語句中加入where條件查詢,預設為true開啟
            selectByExampleQueryId:Select單個對象語句中加入where條件查詢,預設為true開啟
        -->
        <table tableName="user" domainObjectName="User" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="false"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="notify" domainObjectName="Notify" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="false"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="user_follow" domainObjectName="UserFollow" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="false"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
        <table tableName="message" domainObjectName="Message" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true"
               selectByExampleQueryId="false" enableDeleteByPrimaryKey="true" enableUpdateByPrimaryKey="true">
            <property name="my.isgen.usekeys" value="true"/>
            <property name="useActualColumnNames" value="false"/>
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
    </context>
</generatorConfiguration>

運行我們的【MybatisGenerator】類中的 main 方法就能看到自動生成的實體、Xml文件以及 Mapper 類

第三步:Service 層

不給介面了,直接給實現吧,方法都很簡單,而且沒有做任何的安全限制,只是為了實現簡單的消息系統,看效果

UserServiceImpl:

@Service
public class UserServiceImpl implements UserService {
    @Resource
    UserMapper userMapper;
    @Override
    public void addUserByUsername(String username) {
        userMapper.insert(new User(null, username));// 主鍵自增長.
    }
    @Override
    public User findUserById(Long id) {
        return userMapper.selectByPrimaryKey(id);
    }
}

UserFollowServiceImpl:

@Service
public class UserFollowServiceImpl implements UserFollowService {
    @Resource
    UserFollowMapper userFollowMapper;
    @Autowired
    NotifyService notifyService;
    @Override
    public void userAFollowUserBById(Long userAId, Long userBId) {
        // 先要創建一條提示消息
        notifyService.addNotify(userAId, userBId, "follow");// 關註信息
        UserFollow userFollow = new UserFollow();
        userFollow.setUid(userAId);
        userFollow.setFollowUid(userBId);
        userFollowMapper.insertSelective(userFollow);
    }
    @Override
    public void userAUnfollowUserBById(Long userAId, Long userBId) {
        // 首先查詢到相關的記錄
        UserFollowExample example = new UserFollowExample();
        example.or().andUidEqualTo(userAId).andFollowUidEqualTo(userBId);
        UserFollow userFollow = userFollowMapper.selectByExample(example).get(0);
        // 刪除關註數據
        userFollowMapper.deleteByPrimaryKey(userFollow.getId());
    }
}

NotifyServiceImpl:

@Service
public class NotifyServiceImpl implements NotifyService {
    @Resource
    NotifyMapper notifyMapper;
    @Override
    public int addNotify(Long senderId, Long reciverId, String type) {
        Notify notify = new Notify(null, senderId, reciverId, type, false, null);
        return notifyMapper.insertSelective(notify);// id和creatTime自動生成.
    }
    @Override
    public void readNotifyById(Long id) {
        Notify notify = notifyMapper.selectByPrimaryKey(id);
        notify.setIsRead(true);
        notifyMapper.updateByPrimaryKey(notify);
    }
    @Override
    public List<Notify> findAllNotifyByReciverId(Long id) {
        List<Notify> notifies = new LinkedList<>();
        NotifyExample example = new NotifyExample();
        example.setOrderByClause("`id` DESC");// 按id倒敘,也就是第一個數據是最新的.
        example.or().andReciverIdEqualTo(id);
        notifies.addAll(notifyMapper.selectByExample(example));
        return notifies;
    }
    @Override
    public List<Notify> findAllUnReadNotifyByReciverId(Long id) {
        List<Notify> notifies = new LinkedList<>();
        NotifyExample example = new NotifyExample();
        example.setOrderByClause("`id` DESC");// 按id倒敘,也就是第一個數據是最新的.
        example.or().andReciverIdEqualTo(id).andIsReadEqualTo(false);
        notifies.addAll(notifyMapper.selectByExample(example));
        return notifies;
    }
}

MessageServiceImpl:

@Service
public class MessageServiceImpl implements MessageService {
    @Resource
    MessageMapper messageMapper;
    @Resource
    NotifyService notifyService;
    @Override
    public void addMessage(Long senderId, Long reciverId, String content) {
        // 先創建一條 notify 數據
        Long notifyId = (long) notifyService.addNotify(senderId, reciverId, "message");// message表示私信
        // 增加一條私信信心
        Message message = new Message(null, notifyId, senderId, reciverId, content, null);
        messageMapper.insertSelective(message);// 插入非空項,id/createTime資料庫自動生成
    }
    @Override
    public void deleteMessageById(Long id) {
        messageMapper.deleteByPrimaryKey(id);
    }
    @Override
    public Message findMessageByNotifyId(Long id) {
        // 觸發方法時應把消息置為已讀
        notifyService.readNotifyById(id);
        MessageExample example = new MessageExample();
        example.or().andNotifyIdEqualTo(id);
        return messageMapper.selectByExample(example).get(0);
    }
}

第四步:Controller 層

也很簡單,只是為了看效果

UserController:

@RestController
public class UserController {
    @Autowired
    UserService userService;
    @PostMapping("/addUser")
    public String addUser(@RequestParam String username) {
        userService.addUserByUsername(username);
        return "Success!";
    }
    @GetMapping("/findUser")
    public User findUser(@RequestParam Long id) {
        return userService.findUserById(id);
    }
}

UserFollowController :

@RestController
public class UserFollowController {
    @Autowired
    UserFollowService userFollowService;
    @PostMapping("/follow")
    public String follow(@RequestParam Long userAId,
                         @RequestParam Long userBId) {
        userFollowService.userAFollowUserBById(userAId, userBId);
        return "Success!";
    }
    @PostMapping("/unfollow")
    public String unfollow(@RequestParam Long userAId,
                           @RequestParam Long userBId) {
        userFollowService.userAUnfollowUserBById(userAId, userBId);
        return "Success!";
    }
}

NotifyController :

@RestController
public class NotifyController {
    @Autowired
    NotifyService notifyService;
    @PostMapping("/addNotify")
    public String addNotify(@RequestParam Long senderId,
                            @RequestParam Long reciverId,
                            @RequestParam String type) {
        notifyService.addNotify(senderId, reciverId, type);
        return "Success!";
    }
    @PostMapping("/readNotify")
    public String readNotify(@RequestParam Long id) {
        notifyService.readNotifyById(id);
        return "Success!";
    }
    @GetMapping("/listAllNotify")
    public List<Notify> listAllNotify(@RequestParam Long id) {
        return notifyService.findAllNotifyByReciverId(id);
    }
    @GetMapping("/listAllUnReadNotify")
    public List<Notify> listAllUnReadNotify(@RequestParam Long id) {
        return notifyService.findAllUnReadNotifyByReciverId(id);
    }
}

MessageController :

@RestController
public class MessageController {
    @Autowired
    MessageService messageService;
    @PostMapping("/addMessage")
    public String addMessage(@RequestParam Long senderId,
                             @RequestParam Long reciverId,
                             @RequestParam String content) {
        messageService.addMessage(senderId, reciverId, content);
        return "Success!";
    }
    @DeleteMapping("/deleteMessage")
    public String deleteMessage(@RequestParam Long id) {
        messageService.deleteMessageById(id);
        return "Success!";
    }
    @GetMapping("/findMessage")
    public Message findMessage(@RequestParam Long id) {
        return messageService.findMessageByNotifyId(id);
    }
}

第五步:測試

通過 REST 測試工具,可以看到正確的效果,這裡就不給出所有的測試了。

總結

以上的項目簡單而且沒有任何的安全驗證,不過能夠基本完成我們的需求,還有一些功能沒有實現,例如管理員發通告(上面只演示了私信和關註信息),按照上面的系統就直接暴力給每個用戶都加一條通知消息,感覺有點自閉..我也不知道怎麼設計好..希望有經驗的大大能指條路啊!

其實關於這個簡單的系統我查了好多好多資料..把自己都看自閉了,後來我乾脆把所有網頁都關掉,開始用 JPA 自己開始抽象實體,把各個實體寫出來並把所有實體需要的數據啊相互之間的關聯關係啊寫清楚,然後再從自動生成的資料庫中找思路...hhh...要不是我 JPA 不是很熟我覺得用 JPA 就能寫出來了,不用 JPA 的原因在於一些數據的懶載入不知道怎麼處理,還有就是查詢語句太複雜,免不了要浪費一些資源...emmm..說到底還是不是特別懂 JPA,下麵給一張複雜的用 JPA 建立的 User 實體吧(隨手截的..hhh...很亂..):


按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關註公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 方法引用是用來直接訪問類或實例陰莖存在的方法或者構造方法.它需要由相容的函數式介面(lambda表達式中用到的介面)構成的目標類型上下文. 有時候, 當我們想要實現一個函數式介面的方法, 但是已經由類實現了我們想要的功能, 這時可以使用方法引用來直接使用現有的功能實現. 關於lambda表達式 ...
  • 解決跨域的關鍵是設置 Access Control Allow Origin 。 例如:客戶端的功能變數名稱是 api.itbsl.com,而請求的功能變數名稱是www.itbsl.com 如果直接使用ajax訪問,會有以下錯誤: XMLHttpRequest cannot load http://www.itbs ...
  • jinja2模板規範 在當前項目中創建一個文件為templates的文件夾,將其設置為模板文件夾,新建的html為模板頁面, 在視圖函數中使用render_template(".html的文件", my_list=mylist),my_list作為在模板頁面使用的的變數 {{ my_list}} 過... ...
  • Django 系列博客(四) 前言 本篇博客介紹 django 如何和資料庫進行交互並且通過 model 進行數據的增刪查改 ORM簡介 ORM全稱是:Object Relational Mapping(對象關係映射),其主要作用是在編程中,把面向對象的概念跟資料庫中表的概念對應起來。舉例來說就是, ...
  • 黏包現象主要發生在TCP連接, 基於TCP的套接字客戶端往服務端上傳文件,發送時文件內容是按照一段一段的位元組流發送的,在接收方看來,根本不知道該文件的位元組流從何處開始,在何處結束. 兩種黏包現象: 1 連續的小包可能會被優化演算法給組合到一起進行發送 2 第一次如果發送的數據大小2000B接收端一次性 ...
  • 官方寫的方法干擾線是固定的 然後找到captcha/helpers.py 在這個文件添加下麵的函數 ...
  • 一.socketserver模塊 1.sockeserver的源碼流程 2.簡單的使用 socketserver服務端 socket客戶端 二.連接的合法性驗證 1.os.urandom(n)加密 os.urandom(n)是一種bytes類型的隨機生成n個位元組字元串,而且每次生成的值都不相同,再加 ...
  • 一、編寫一個簡單程式,要求數組長度為5,分別賦值10,20,30,40,50,在控制台輸出該數組的值。 package com.test; public class t01 { public static void main(String[] args) { // 靜態初始化 int i[] = n ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...