5.实战内容【动态系统】

终于到了本部分结尾,从开始写这部分内容到现在花了好久时间,几乎每天都不做别的事情,不知道写的内容有没有问题,有没有通俗易懂,不过反正我也是小白,有问题还需要请读者反馈,谢谢!

实战内容来做什么呢?我们就来实现一个简单的朋友圈的功能。

当然对于这部分内容,我们仅仅实现发动态,改动态,删动态,查看动态这四个功能,其他的点赞,评论的功能其实也可以做,但是跨度太大,怕一时不能接受,您可以自行设计。

所有的源代码请参考 github,我会在文章结尾贴。

国际惯例

准备些什么东西?

编码:Idea

测试:PostMan

数据库:Mysql及Mysql管理工具

1.需求及数据表设计

对于动态,简化一下过程,设定只允许发文字动态,不允许有图片

那么对于一条动态,我们应该有以下几个字段

主键 动态内容 发送者ID 点赞数 评论数
id text user_id love_num review_num

一共5个字段,我们不光有动态内容,也要有一个user_id来标识是谁发的,另外点赞数和评论数都是动态必有的属性。

我们先建立该表,详见下表

字段名 类型 排序规则 注释 其他
id int4 自增长主键 不为空
text varchar100 utf8mb4_general_ci 动态内容 无默认值,不为空
user_id int4 发送者id 无默认值,不为空
love_num int4 点赞数 默认值为0
review_num int4 评论数 默认值为0

数据表就算设计完成了,下面我们新建一个项目。

2. 新建项目

新建一个项目,然后在目录下建立如下文件。

1547949436448

可以看到,我们建立了几个包,Controller,Entities,Mapper,Service,这里你没见过的就是Service,这里是为了逻辑分而建立的,我们把Controller里的逻辑移到Service里面写的,一会就可以看到这样写的好处。

然后分别在包下添加对应文件。

接下来我们导入mysql和mybatis的依赖,前面提到过就不再啰嗦了。

同时我们填入相应的数据库属性,如果还不明白的话,请参考数据库请求一节。

唯一想说的是,注意一下不要原模原样复制属性值,因为你的包名很可能不一样了。

我在综合提一下需要你提前配置好的东西:

  • 持久化依赖
  • 数据库属性配置
  • Application中的Mapper映射。

配置好了,你可以写一个HelloWorld接口测试一下。具体参考之前的,注意不要忘记Controller里的注解。

可以通过,我们就开始写接口。

3. 实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package cn.surine.moment_demo.Entities;

public class MomentEntity {

private int id;
private String text;
private int userId;
private long loveNum;
private long reviewNum;

public MomentEntity() {
}

public MomentEntity(int id, String text, int userId, long loveNum, long reviewNum) {
this.id = id;
this.text = text;
this.userId = userId;
this.loveNum = loveNum;
this.reviewNum = reviewNum;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}

public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

public long getLoveNum() {
return loveNum;
}

public void setLoveNum(long loveNum) {
this.loveNum = loveNum;
}

public long getReviewNum() {
return reviewNum;
}

public void setReviewNum(long reviewNum) {
this.reviewNum = reviewNum;
}
}

实体类里有几个字段,分别对应了前面我们分析的数据表的内容,这里我们写了一个空构造,和一个完整的构造方法,toString方法可以自行添加,方便测试,这里我就不写了。

另外这里有一个小技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package cn.surine.moment_demo.Entities;

public class JsonUtil {
private int jCode; //状态码
private String jMsg; //状态信息
private Object jData; //携带的数据

public JsonUtil(int jCode, String jMsg, Object jData) {
this.jCode = jCode;
this.jMsg = jMsg;
this.jData = jData;
}

public void setOk(Object data){
this.jCode = 2000;
this.jMsg = "操作成功";
this.jData = data;
}

public void setFail(Object data){
this.jCode = 4000;
this.jMsg = "操作失败";
this.jData = data;
}

public JsonUtil() {
}

public int getjCode() {
return jCode;
}

public void setjCode(int jCode) {
this.jCode = jCode;
}

public String getjMsg() {
return jMsg;
}

public void setjMsg(String jMsg) {
this.jMsg = jMsg;
}

public Object getjData() {
return jData;
}

public void setjData(Object jData) {
this.jData = jData;
}
}

我们可以继续写这样一个实体类,用于封装返回前端的Json内容,可以一次性携带请求状态,状态信息和数据等内容,当然你可以随意封装,写几个静态方法方便调用,下面我们可以看到具体的使用场景

4.发送动态接口

注意写完一部分内容看效果要重启工程哦。

从Mapper入手。

1
2
3
4
5
6
7
8
9
10
11
public interface MomentMapper {

/**
* 发送动态
* @param momentEntity 动态实体
* @return 操作状态
* */
@Insert("INSERT INTO moment(text,user_id) VALUES(#{text}, #{userId})")
int insert(MomentEntity momentEntity);

}

相信你已经很熟悉了,这边是插入一条内容,但是love_num,review_num是我们用于标识点赞数和评论数的,结合我们实际情况,我们不能发送动态的时候就传入。

再看Controller。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RestController
@EnableAutoConfiguration
@RequestMapping("/moment")
public class MomentController {

@Autowired
private MomentService momentService;

/**
* 插入数据
* @param text 文本内容
* @param uid 发送者用户id
* @return JsonUtil
* */
@RequestMapping(value = "/sendMoment",method = RequestMethod.POST)
public JsonUtil sendMoment(
@RequestParam(value = "text") String text,
@RequestParam(value = "uid") int uid
){
MomentEntity momentEntity = new MomentEntity();
momentEntity.setText(text);
momentEntity.setUserId(uid);
return momentService.sendMoment(momentEntity);
}
}

内容貌似有点多,但是重复的内容还是不少。

@RequestMapping(“/moment”)的加入,给本Controller也添加了访问路径了,相当于加了一个目录,我们可以通过多级路径来访问它

1
localhost:8080/moment/sendMoment

下面是自动注入部分,之前我们一直直接注入Mapper,现在我们规范了写法,需要注入Service

1
2
@Autowired
private MomentService momentService;

其他的就没什么了,POST接受参数,并组装成对象,然后调用了service的sendMoment方法,就可以把数据发送到service里,那我们看看service都做了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Service
public class MomentService {

/**
* 注入Mapper,并且新建立一个JsonUtil对象,用于返回json封装
*/
@Autowired
private MomentMapper momentMapper;
JsonUtil json = new JsonUtil();


/**
* 插入数据
* @param momentEntity 动态实体
* @return JsonUtil
* */
public JsonUtil sendMoment(MomentEntity momentEntity){
//检测参数是否为空,参数空的话,就提示前台,我们一会看效果
if(momentEntity == null || momentEntity.getText().isEmpty()){
json.setEmptyParam(null);
return json;
}
//正式插入,调用momentMapper的insert方法
int status = momentMapper.insert(momentEntity);
//插入状态
if(status >= 1){
json.setOk(null);
}else{
json.setFail(null);
}
//返回数据
return json;
}
}

注释写的已经很详细了,这里我们在service里面注入了mapper,并且封装了jsonUtil用于传递数据,接下来的逻辑就是动态内容判空(即不允许发送空白动态,如果是空白的,就返回提示),调用了插入方法之后,会有返回状态,以前都提过。

这样,我们就联通起来了。

我们看测试环境

测试内容:localhost:8080/moment/sendMoment

text = 撑死了,uid = 4

测试结果 如下(数据库里最后一条)

1547978763297

1547978894632

测试内容:localhost:8080/moment/sendMoment

text = “” uid = 4

测试结果如下,可以看到参数为空的提示

1547978971211

5.删除动态

下面我们实现按照id来删除动态。

Mapper内添加:

1
2
3
4
5
6
7
8
/**
* 删除动态
* @param momentEntity 动态实体
* @return 操作状态
* 删除数据库内id = ? and user_id = ? 的数据
* */
@Delete("DELETE from moment where id = #{id} AND user_id = #{userId}")
int delete(MomentEntity momentEntity);

Controller内添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 删除动态
* @param id 动态id
* @param uid 发送者用户id
* @return JsonUtil
* */
@RequestMapping(value = "/deleteMoment")
public JsonUtil deleteMoment(
@RequestParam(value = "id") int id,
@RequestParam(value = "uid") int uid
){
MomentEntity momentEntity = new MomentEntity();
momentEntity.setId(id);
momentEntity.setUserId(uid);
return momentService.deleteMoment(momentEntity);
}

与之前类似,不解释了,get传入id和uid

Service 内添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 删除数据
* @param momentEntity 动态实体
* @return JsonUtil
* */
public JsonUtil deleteMoment(MomentEntity momentEntity){
//检测参数是否为空
if(momentEntity == null){
json.setEmptyParam(null);
return json;
}
//正式删除
int status = momentMapper.delete(momentEntity);
//状态
if(status >= 1){
json.setOk(null);
}else{
json.setFail(null);
}
return json;
}

测试内容:

localhost:8080/moment/deleteMoment?id=2&uid=1

返回结果如下,数据库如下

1547980104675

1547980144338

我们可以对比之前的数据内容,id=2 ,uid = 1 的内容被我们删除了。

我们再来看看没有此条数据是什么情况:

localhost:8080/moment/deleteMoment?id=4&uid=1

1547980233659

我们在实际开发中,所考虑的不能是这么简单,还需要用户验证之类的,但是至少要保证,要删除的id 和uid对应能找到这么一条数据,否则就出问题了(但几乎是没啥问题,以防万一而已)

6.修改动态

我们按id,uid来匹配动态,并且传入修改的内容。

Mapper内添加:

1
2
3
4
5
6
7
8
/**
* 更新动态
* @param momentEntity 动态实体
* @return 操作状态
* 更新id = ?and uid = ?的数据的text内容
* */
@Update("UPDATE moment SET text=#{text} where id = #{id} AND user_id = #{userId}")
int update(MomentEntity momentEntity);

Controller内添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 更新动态
* @param id 动态id
* @param uid 发送者用户id
* @param text 动态新内容
* @return JsonUtil
* */
@RequestMapping(value = "/updateMoment",method = RequestMethod.POST)
public JsonUtil updateMoment(
@RequestParam(value = "id") int id,
@RequestParam(value = "uid") int uid,
@RequestParam(value = "text") String text
){
MomentEntity momentEntity = new MomentEntity();
momentEntity.setId(id);
momentEntity.setUserId(uid);
momentEntity.setText(text);
return momentService.updateMoment(momentEntity);
}

Service内添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 更新数据
* @param momentEntity 动态实体
* @return JsonUtil
* */
public JsonUtil updateMoment(MomentEntity momentEntity){
//检测参数是否为空
if(momentEntity == null || momentEntity.getText().isEmpty()){
json.setEmptyParam(null);
return json;
}
//正式更新
int status = momentMapper.update(momentEntity);
//状态
if(status >= 1){
json.setOk(null);
}else{
json.setFail(null);
}
return json;
}

测试内容:

localhost:8080/moment/updateMoment

id = 6 uid =4 text = 我没吃饱怎么办

操作结果与数据库如下

1547981034775

1547981088651

测试内容2

1547981128506

测试内容3

1547981162405

我很想解释一波,但是并木有什么可以解释的。

下面我们就来实现最后一个吧—–查询

7.查询

国际惯例 按id查询,和全列表查询

按id查询Mapper

1
2
3
4
5
6
7
8
/**
* 查询动态
* @param momentEntity 动态实体
* @return momentEntity 动态实体
* 查询id = ? and uid = ? 的所有动态
* */
@Select("SELECT * FROM moment where id = #{id} AND user_id = #{userId}")
MomentEntity selectOne(MomentEntity momentEntity);

Controller添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 按照id获取动态
* @param id 动态id
* @param uid 发送者用户id
* @return JsonUtil
* */
@RequestMapping(value = "/getOneMoment")
public JsonUtil getOneMoment(
@RequestParam(value = "id") int id,
@RequestParam(value = "uid") int uid
){
MomentEntity momentEntity = new MomentEntity();
momentEntity.setId(id);
momentEntity.setUserId(uid);
return momentService.getOneMoment(momentEntity);
}

Service如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 查询单条数据
* @param momentEntity 动态实体
* @return JsonUtil
* */
public JsonUtil getOneMoment(MomentEntity momentEntity){
//检测参数是否为空
if(momentEntity == null){
json.setEmptyParam(null);
return json;
}
MomentEntity seletMoment= momentMapper.selectOne(momentEntity);
//状态
if(seletMoment != null){
json.setOk(seletMoment);
}else{
json.setFail(null);
}
return json;
}

这里唯一的不同 就是查询到数据,会把数据返回给前端。

测试结果:

localhost:8080/moment/getOneMoment?id = 5 & uid = 3

测试结果如下,返回数据库相应数据,这里截图中是post方法,我的接口写的是get方法,其实不要被它迷惑了,postman的强大甚至可以给你自行改post为get ,哈哈哈哈…… 都一样,喜欢什么用什么即可。

1547981677784

另一种结果:

1547981823867

这样你就实现了,按照id来查询动态的功能。

查询所有数据写法如下

Mapper:

1
2
3
4
5
/**
* 查询所有动态
* */
@Select("SELECT * FROM moment")
List<MomentEntity> selectAll();

Controller:

1
2
3
4
5
6
7
8
/**
* 获取动态列表
* @return JsonUtil
* */
@RequestMapping(value = "/getMoments")
public JsonUtil getMoments(){
return momentService.getMoments();
}

Service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 查询所有数据
* @return JsonUtil
* */
public JsonUtil getMoments(){
List<MomentEntity> momentEntityList = momentMapper.selectAll();
//状态
if(momentEntityList != null){
json.setOk(momentEntityList);
}else{
json.setFail(null);
}
return json;
}

将momentEntityList传入返回对象中,即可返回给前端。

测试:请求地址:localhost:8080/moment/getMoments及测试结果

1547986236848

额,这个实战写了好久好久……终于写完了。

不知道有没有什么错误,如果有问题或者疑问,还请小可爱们指出哦,核心源代码我会上传的。

8.总结

可能你做完这个觉得还是不爽,没有看到界面,只看到一堆乱七八糟的数据符号,根本就没有微信朋友圈的效果,不过我可以明确的告诉你,做APP后端,看到这些数据就已经OK了。

当然这些东西仅限于简单数据,还有复杂的数据参数传递之类的我会在后边再提。

学会这些就稍微放松一下吧!

或者自己尝试再实现一些小功能。

接下来我们会学习一些枯燥的东西。

本部分内容代码:https://github.com/Surine/SuSuBoot

点我回到目录