likes
comments
collection
share

痛定思痛!我写起了后端代码。

作者站长头像
站长
· 阅读数 21

前言

相信很多人同我一样,喜欢给自己制定一些TODO List,以及一些打卡任务,例如我给自己制定了这么一个打卡任务:记单词100个,目标打卡天数为100天,任务周期为2023年这一整年。这时候就需要有一个工具来帮我们记录打卡任务的完成进度了。我相信市面上肯定有很多这种小工具,但自己亲手实现一个,会更加的灵活,且意义非凡。

JUST DO IT。💪

每日打卡

先来展示一下成果吧,下方是每日打卡APP内的几张截图。

每日任务添加计划每日任务展示
痛定思痛!我写起了后端代码。痛定思痛!我写起了后端代码。痛定思痛!我写起了后端代码。
年度计划当日计划展示计划总展示
痛定思痛!我写起了后端代码。痛定思痛!我写起了后端代码。痛定思痛!我写起了后端代码。

一开始,我只想做一个本地应用,在APP内搭建数据库来存储数据,心想这样更加的安全(主要我不会后端技术🐶)。但随着我个人的实际使用体验下来,不联网的弊端十分明显,哪天要是不小心将APP删除了,那么你的所有打卡数据就都无了。在我将每日打卡APP误删除后,痛定思痛,决定研究一下后端开发。

所以本篇文章,我主要想分享一下我的后端生涯的第一行”HELLO WORLD”代码。

上手实践

我采用的是 Spring Boot + MySql + MyBatis 技术栈,先来看看项目结构👇。

痛定思痛!我写起了后端代码。

需求分析

对于客户端来说,我们有添加任务、修改任务、删除任务以及UI交互上的这些需求,但站在后端角度,这就是一个很简单的增删改查操作,只需要处理一下数据,然后输出几个接口供客户端调用即可。

创建表

表的内容要包含:唯一标识id、任务名称、打卡目标天数、打卡进度、创建日期、结束日期、是否完成打卡任务。

确定好内容以后,我们就可以编写创建表的Sql语句了。

CREATE TABLE `year_task` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `task_name` varchar(100) NOT NULL,
  `target` int NOT NULL,
  `progress` int NOT NULL,
  `create_date` date DEFAULT NULL,
  `finish_date` date DEFAULT NULL,
  `is_finish` tinyint DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb3

创建完成后,我们使用 desc year_task; 语句来展示一下 year_task 数据库表结构。

mysql> desc year_task;

+-------------+--------------+------+-----+---------+----------------+

| Field       | Type         | Null | Key | Default | Extra          |

+-------------+--------------+------+-----+---------+----------------+

| id          | bigint       | NO   | PRI | NULL    | auto_increment |

| task_name   | varchar(100) | NO   |     | NULL    |                |

| target      | int          | NO   |     | NULL    |                |

| progress    | int          | NO   |     | NULL    |                |

| create_date | date         | YES  |     | NULL    |                |

| finish_date | date         | YES  |     | NULL    |                |

| is_finish   | tinyint      | YES  |     | 0       |                |

+-------------+--------------+------+-----+---------+----------------+

7 rows in set (0.00 sec)

OK,表创建好了,接下来就是在项目中定义一个对应的Bean对象YearTaskBean

public class YearTaskBean {
    
    private Long id;

    @NotBlank(message = "taskName is not blank")
    private String taskName;

    @NotNull(message = "target not null")
    private Integer target;

    @NotNull(message = "progress not null")
    private Integer progress;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date createDate;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date finishDate;

    private Boolean isFinish;

}

字段说明:

  • id:唯一标识。
  • taskName:任务名称。如:练习篮球。
  • target:打卡的目标天数。如:60,标识打卡的目标天数为60天。
  • progress:打卡进度。如:15,标识打卡了15天。
  • createDate:创建日期。如:2023-06-12。
  • finishDate:结束日期。如:2023-09-19
  • isFinish:表示任务是否结束。

所有的代码下载地址会在下方给出,这里我只展示核心代码。

Service

我们先定义一个 YearTaskService 接口。

public interface YearTaskService {

    /**
     * 获取所有的任务
     *
     * @return
     */
    List<YearTaskBean> getAllYearTasks();

    /**
     * 通过ID获取指定的任务
     *
     * @param id
     * @return
     */
    YearTaskBean getYearTaskById(Long id);

    /**
     * 通过打卡任务名来获取指定的任务列表
     *
     * @param taskName
     * @return
     */
    List<YearTaskBean> getYearTaskByName(String taskName);

    /**
     * 通过指定的条件来获取相对应的任务
     *
     * @param conditionMap
     * @return
     */
    YearTaskBean getYearTaskByMap(Map<String, Object> conditionMap);

    /**
     * 添加任务
     *
     * @param yearTaskBean 有客户端请求参数反序列化而来
     */
    void insertYearTask(YearTaskBean yearTaskBean);

    /**
     * 根据给定的条件插入Task。
     *
     * @param insertConditionMap 例如:[taskName=百词斩记单词, target=21]
     */
    void insertYearTaskByMap(Map<String, Object> insertConditionMap);

    /**
     * 更新任务
     *
     * @param yearTaskBean
     */
    void updateYearTask(YearTaskBean yearTaskBean);

    /**
     * 通过ID删除指定任务
     *
     * @param id
     */
    void deleteYearTaskById(Long id);
}

然后先建一个YearTaskServiceImpl类来具体实现该接口。

@Service
public class YearTaskServiceImpl implements YearTaskService {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private YearTaskMapper yearTaskMapper;

    @Override
    public List<YearTaskBean> getAllYearTasks() {
        return yearTaskMapper.getAllYearTasks();
    }

    @Override
    public YearTaskBean getYearTaskById(Long id) {
        return yearTaskMapper.getYearTaskById(id);
    }

    @Override
    public List<YearTaskBean> getYearTaskByName(String taskName) {
        return yearTaskMapper.getYearTaskByName(taskName);
    }

    @Override
    public YearTaskBean getYearTaskByMap(Map<String, Object> conditionMap) {
        return yearTaskMapper.getYearTaskByMap(conditionMap);
    }

    @Override
    public void insertYearTask(YearTaskBean yearTaskBean) {
        yearTaskMapper.insertYearTask(yearTaskBean);
    }

    @Override
    public void insertYearTaskByMap(Map<String, Object> insertConditionMap) {
        int target = Integer.parseInt(insertConditionMap.get("target").toString());
        int progress = 0;
        if (insertConditionMap.get("progress") != null) {
            progress = Integer.parseInt(insertConditionMap.get("progress").toString());
        }

        boolean isFinish = false;
        Date finishDate = null;
        if (progress >= target) {
            isFinish = true;
            finishDate = new Date();
        }

        YearTaskBean yearTaskBean = new YearTaskBean(null,
                insertConditionMap.get("taskName").toString(),
                target,
                progress,
                new Date(),
                finishDate,
                isFinish);

        yearTaskMapper.insertYearTask(yearTaskBean);
    }

    @Override
    public void updateYearTask(YearTaskBean yearTaskBean) {
        yearTaskMapper.updateYearTask(yearTaskBean);
    }

    @Override
    public void deleteYearTaskById(Long id) {
        yearTaskMapper.deleteYearTaskById(id);
    }
}

YearTaskServiceImpl类中可以看到,其方法里面是都是调用YearTaskMapper里面的方法。而这个Mapper正是与数据库的连接桥,我们需要定义相对应的SQL语句在其中。

Mapper

先先建一个YearTaskMapper接口定义好所有的方法。

public interface YearTaskMapper {

    /**
     * 查询所有 year task
     *
     * @return
     */
    List<YearTaskBean> getAllYearTasks();

    /**
     * 通过指定id来查询 year task
     *
     * @param id
     * @return
     */
    YearTaskBean getYearTaskById(Long id);

    /**
     * 通过指定taskName来查询 year task
     *
     * @param taskName
     * @return
     */
    List<YearTaskBean> getYearTaskByName(String taskName);

    /**
     * 根据指定条件来查询 year task,例如:使用 taskName 与 createDate 来查询
     *
     * @param conditionMap
     * @return
     */
    YearTaskBean getYearTaskByMap(Map<String, Object> conditionMap);

    /**
     * 插入指定 year task
     *
     * @param yearTaskBean
     */
    void insertYearTask(YearTaskBean yearTaskBean);

    /**
     * 更新 year task
     *
     * @param yearTaskBean
     */
    void updateYearTask(YearTaskBean yearTaskBean);

    /**
     * 通过 id 来删除指定 year task
     *
     * @param id
     */
    void deleteYearTaskById(Long id);
}

然后创建一个YearTaskMapper.xml文件来定义具体的SQL语句。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.day_record.server.mapper.YearTaskMapper">

    <select id="getAllYearTasks" resultType="com.day_record.server.bean.YearTaskBean">
        SELECT * FROM year_task
    </select>

    <select id="getYearTaskById" resultType="com.day_record.server.bean.YearTaskBean">
        SELECT * FROM year_task WHERE id=#{id}
    </select>

    <select id="getYearTaskByName" resultType="com.day_record.server.bean.YearTaskBean">
        SELECT * FROM year_task WHERE task_name=#{taskName}
    </select>

    <select id="getYearTaskByMap" parameterType="java.util.Map" resultType="com.day_record.server.bean.YearTaskBean">
        SELECT * FROM year_task WHERE task_name=#{taskName} AND create_date=#{createDate}
    </select>

    <insert id="insertYearTask" parameterType="com.day_record.server.bean.YearTaskBean">
        insert into year_task (id, task_name, target, progress, create_date, finish_date, is_finish)
        value (#{id}, #{taskName}, #{target}, #{progress}, #{createDate}, #{finishDate}, #{isFinish})
    </insert>

    <update id="updateYearTask">
        update year_task set task_name=#{taskName}, target=#{target}, progress=#{progress}, create_date=#{createDate}, finish_date=#{finishDate}, is_finish=#{isFinish} where id=#{id}
    </update>

    <delete id="deleteYearTaskById">
        delete from year_task where id=#{id}
    </delete>

</mapper>

写好了SQL语句,拿到了数据,接下来就是定义对外的API了,供客户端使用。

Controller

一般会以Controller为后缀来命名,如:TaskController。在该类中,我们会通过@Autowired注解注入YearTaskService,然后调用其方法。

以获取所有任务来举例:

@GetMapping("/year/get_all_year_tasks")
public BaseBean<List<YearTaskBean>> getAllYearTasks() {
    List<YearTaskBean> allYearTasks = yearTaskService.getAllYearTasks();
    return new BaseBean<>(200, allYearTasks, "success");
}

我们运行代码,然后再Postman中进行调试接口。

痛定思痛!我写起了后端代码。

再来一个POST请求,以插入任务来举例:

@PostMapping("/year/insert_year_task")
public BaseBean<String> insertYearTask(YearTaskBean yearTaskBean) {
    String msg;
    logger.info("insertYear Task -> " + yearTaskBean.toString());
    try {
        yearTaskService.insertYearTask(yearTaskBean);
        msg = "insert (" + yearTaskBean.getTaskName() + ") success";
    } catch (Exception exception) {
        msg = "insert (" + yearTaskBean.getTaskName() + ") error: " + exception.getCause().getMessage();
    }

    return new BaseBean<>(msg);
}

同样在Postman上调试一下。

痛定思痛!我写起了后端代码。

Nice,终于有一天,我们前端同学也可以给自己写接口了🤓。

总结

本文主要是分享作者作为前端同学,在初学后端技术后,输出的第一行代码。

本文涉及的所有代码,都已开源,如有需要点击这里自取。创作分享不易,如果有帮助到你,希望可以给我点个Star,十分感谢。

如果屏幕前的你同样对后端技术感兴趣,欢迎一起探讨学习呀。

到此文章就结束啦~

其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。

另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~✌️!