Project Development Notes - Chuanzhi health III: drawing bed and regular waste disposal

summary

In fact, this part is also a one to many CRUD. Different from the simple field content of the inspection team, it involves the processing of images. First, let's take a look at how the front end describes the image upload part and the corresponding data binding

<el-form-item label="Upload pictures">
    <el-upload
               class="avatar-uploader"
               action="/setMeal/upload.do"
               :auto-upload="autoUpload"
               name="imgFile"
               :show-file-list="false"
               :on-success="handleAvatarSuccess"
               :before-upload="beforeAvatarUpload">
        <img v-if="imageUrl" :src="imageUrl" class="avatar">
        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
</el-form-item>
        data: {
            autoUpload: true,//Automatic upload
            imageUrl: null,//Model data, which is used for picture preview after uploading pictures
            activeName: 'first',//Add / edit window Tab label name
            pagination: {//Paging related properties
                currentPage: 1,
                pageSize: 10,
                total: 100,
                queryString: null,
            },
            dataList: [],//List data
            formData: {},//Form Data 
            tableData: [],//Check group list data in the add form window
            checkgroupIds: [],//id corresponding to the check group check box in the add form window
            dialogFormVisible: false, //Controls the display / hiding of the add window
            dialogFormVisible4Edit: false //Controls the display / hiding of the edit window
        },

Here are several items worth noting. action represents the image upload path, auto upload represents enabling automatic upload, name represents the file name: on success and before upload, the methods executed after and before the successful upload of the image, and imgUrl represents the path of the image. See the following separately

    //The hook after the file is uploaded successfully. The response is the value returned by the server, and the file is the js object encapsulated by the currently uploaded file
    handleAvatarSuccess(response, file) {
        //Assign value to model data for page image preview
        this.imageUrl = 'http://qu0dxvmvo.hd-bkt.clouddn.com/' + response.data;
        this.$message({
            type: response.flag ? 'success' : 'error',
            message: response.message
        });
        //Set the picture name to facilitate img storage in the database
        this.formData.img = response.data;
    },
        //Execute before uploading pictures
        beforeAvatarUpload(file) {
            const isJPG = file.type === 'image/jpeg';
            const isLt2M = file.size / 1024 / 1024 < 2;
            if (!isJPG) {
                this.$message.error('Upload package pictures can only be JPG format!');
            }
            if (!isLt2M) {
                this.$message.error('Upload package picture size cannot exceed 2 MB!');
            }
            return isJPG && isLt2M;
        },
  • The format and size are verified before uploading

  • After the upload is successful, the url of the image is assigned to facilitate the preview. More importantly, the img attribute of the form data is assigned to the picture name stored in the picture bed to facilitate the body communication of the form

The imageUrl here is the external chain provided by the object storage service of qiniu cloud. The external chain plus the picture name is the URL path to access the picture. We can easily find that as long as we store the name of the picture in the database, we can use the name of the picture to display the picture. In this way, the CRUD of the picture is changed to an ordinary field CRUD, and the rest of the processing is the same as the previous inspection items and inspection groups. However, since we want to use the image name to uniquely locate the image, we must ensure that the image name is unique. We use UUID to deal with it. The corresponding CRUD is given below

Front end CRUD

<script>
    var vue = new Vue({
        el: '#app',
        data: {
            autoUpload: true,//Automatic upload
            imageUrl: null,//Model data, which is used for picture preview after uploading pictures
            activeName: 'first',//Add / edit window Tab label name
            pagination: {//Paging related properties
                currentPage: 1,
                pageSize: 10,
                total: 100,
                queryString: null,
            },
            dataList: [],//List data
            formData: {},//Form Data 
            tableData: [],//Check group list data in the add form window
            checkgroupIds: [],//id corresponding to the check group check box in the add form window
            dialogFormVisible: false, //Controls the display / hiding of the add window
            dialogFormVisible4Edit: false //Controls the display / hiding of the edit window
        },
        created() {
            this.findPage();
        },
        methods: {
            //The hook after the file is uploaded successfully. The response is the value returned by the server, and the file is the js object encapsulated by the currently uploaded file
            handleAvatarSuccess(response, file) {
                //Assign value to model data for page image preview
                this.imageUrl = 'http://qu0dxvmvo.hd-bkt.clouddn.com/' + response.data;
                this.$message({
                    type: response.flag ? 'success' : 'error',
                    message: response.message
                });
                //Set the picture name to facilitate img storage in the database
                this.formData.img = response.data;
            },
            //Execute before uploading pictures
            beforeAvatarUpload(file) {
                const isJPG = file.type === 'image/jpeg';
                const isLt2M = file.size / 1024 / 1024 < 2;
                if (!isJPG) {
                    this.$message.error('Upload package pictures can only be JPG format!');
                }
                if (!isLt2M) {
                    this.$message.error('Upload package picture size cannot exceed 2 MB!');
                }
                return isJPG && isLt2M;
            },
            //Edit options submit form
            handleEdit() {
                axios.post("/setMeal/edit.do?checkGroupIds=" + this.checkgroupIds, 						this.formData).then((res) => {
                    if (res.data.flag == true) {
                        //Pop up prompt
                        this.$message({
                            message: res.data.message,
                            type: 'success'
                        });
                        //Close the edit window
                        this.dialogFormVisible4Edit = false;
                        this.findPage();
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
            },
            //add to
            handleAdd() {
                axios.post("/setMeal/add.do?checkGroupIds=" + this.checkgroupIds, 						this.formData).then((res) => {
                    if (res.data.flag == true) {
                        //Pop up prompt
                        this.$message({
                            message: res.data.message,
                            type: 'success'
                        });
                        //Close new window
                        this.dialogFormVisible = false;
                        this.findPage();
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
            },
            handleDelete(row) {
                this.$confirm("Are you sure to delete?", "Tips", {
                    type: 'warning'
                }).then(() => {
                    var setMealId = row.id;
                    //Send asynchronous request
                    //If you still use double variables, it will return null
                    axios.post("/setMeal/delete.do?setMealId=" + setMealId).then((res) => 						{
                        //Process according to the data return
                        if (res.data.flag == true) {
                            this.$message({
                                message: res.data.message,
                                type: 'success'
                            });
                            this.findPage();
                        } else {
                            this.$message({
                                message: res.error(data.message),
                            });
                        }
                    });
                }).catch(() => {
                    this.$message({
                        message: 'Operation cancelled',
                        type: 'info'
                    });
                })
            },
            //Paging query
            findPage() {
                var param = {
                    currentPage: this.pagination.currentPage,
                    pageSize: this.pagination.pageSize,
                    queryString: this.pagination.queryString
                }
                axios.post("/setMeal/findPage.do", param).then((res) => {
                    this.pagination.total = res.data.total;
                    this.dataList = res.data.rows;
                });
            },
            // Reset Form 
            resetForm() {
                this.imageUrl = null;
                this.formData = {};
                this.checkgroupIds = [];
                this.activeName = 'first';
            },
            // The add window pops up
            handleCreate() {
                this.resetForm();
                this.dialogFormVisible = true;
                axios.get("/checkGroup/findAll.do").then((res) => {
                    if (res.data.flag == true) {
                        this.tableData = res.data.data;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
            },
            //Pop up the editing window to complete the dynamic display of data
            handleUpdate(row) {
                var setMealId = row.id;
                this.resetForm();

                //Get form data
                axios.get("/setMeal/findById.do?setMealId=" + setMealId).then((res)=>{
                    if (res.data.flag == true) {
                        //Form data assignment
                        this.formData = res.data.data;
                        //Picture preview update
                        this.imageUrl = 'http://qu0dxvmvo.hd-bkt.clouddn.com/' + 												res.data.data.img;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
                //Get all options
                axios.get("/checkGroup/findAll.do").then((res) => {
                    if (res.data.flag == true) {
                        this.tableData = res.data.data;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });
                //Get selected
                axios.get("/setMeal/findRelationship.do?setMealId=" + 										setMealId).then((res)=>{
                    if (res.data.flag == true) {
                        //Array assignment
                        this.checkgroupIds = res.data.data;
                    } else {
                        this.$message.error(res.data.message);
                    }
                });

                this.dialogFormVisible4Edit = true;
            },
            //Switch page number
            handleCurrentChange(currentPage) {
                this.pagination.currentPage = currentPage;
                this.findPage();
            }
        }
    })
</script>

You can see that in the front-end part, it is no different from the inspection items of the inspection team, except that the form is updated and the preview is generated after the picture is uploaded

Note the selection of HTTP request method here. GET is used for request parameters and POST is used for obtaining parameters. The details are described in more detail in the Java Web learning notes

Control layer CRUD

Previously, the img field has been used to make one-to-one correspondence between the picture and the picture in qiniu cloud storage. Therefore, the CRUD of the package is basically the same as the previous inspection item inspection group. However, in addition to the processing field, it is also necessary to upload the picture to the server. In the picture upload component, the path is bound as / setMeal/upload. The corresponding processing method is written in the control layer as follows

    //Upload pictures to qiniu cloud
    @RequestMapping("/upload")
    public Result upload(MultipartFile imgFile) {
        //Get the file name and intercept the suffix
        String originalFilename = imgFile.getOriginalFilename();
        int index = originalFilename.lastIndexOf('.');
        String extension = originalFilename.substring(index - 1);
        //Use UUID to generate file names and retain suffixes
        String fileName = UUID.randomUUID().toString() + extension;

        try {
            //Upload using qiniu cloud component
            QiniuUtils.upload2Qiniu(imgFile.getBytes(), fileName);
            return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,fileName);
        } catch (IOException e) {
            e.printStackTrace();
            return new Result(false,MessageConstant.PIC_UPLOAD_FAIL);
        }
    }

The relevant upload methods of qiniu cloud have been encapsulated, and the specific implementation can be found on qiniu's official website.

However, there is a problem with this processing: our CRUD processes the fields, and the pictures stored in the server have not been deleted. Even if the form is not submitted, simply uploading and previewing the pictures will store the pictures in the server, which will cause a large number of garbage pictures to accumulate. Therefore, we need to design methods to process the garbage pictures, The method we adopt here is to design two set sets in Redis, one to store the name of the picture stored in the database processed by CRUD and the other to process the name of all the pictures uploaded to the server. We can get which are spam pictures by taking the difference between the two.

We will use Quartz timing to clean up the garbage images later. In the control layer, the business layer only needs to maintain two Redis collections. Therefore, the image upload method is modified as follows. First, configure jedis in Spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--Jedis Related configuration of connection pool-->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal">
            <value>200</value>
        </property>
        <property name="maxIdle">
            <value>50</value>
        </property>
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="true"/>
    </bean>
    <!--use windows of Redis,therefore ip 127.0.0.1-->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
        <constructor-arg name="host" value="127.0.0.1" />
        <constructor-arg name="port" value="6379" type="int" />
        <constructor-arg name="timeout" value="30000" type="int" />
    </bean>
</beans>

Add a sentence after uploading

jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES, fileName);

Of course, Autowired should also be used in the method

@Autowired
private JedisPool jedisPool;

In this way, the image upload is done, and then the simple CRUD is done. The complete control layer code is given below

package com.cui.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.cui.constant.MessageConstant;
import com.cui.constant.RedisConstant;
import com.cui.entity.PageResult;
import com.cui.entity.QueryPageBean;
import com.cui.entity.Result;
import com.cui.pojo.Setmeal;
import com.cui.service.SetMealService;
import com.cui.utils.QiniuUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.JedisPool;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * Package management
 */

@RestController
@RequestMapping("/setMeal")
public class SetMealController {

    @Reference
    private SetMealService setMealService;

    @Autowired
    private JedisPool jedisPool;

    //Upload pictures to qiniu cloud
    @RequestMapping("/upload")
    public Result upload(MultipartFile imgFile) {
        //Get the file name and intercept the suffix
        String originalFilename = imgFile.getOriginalFilename();
        int index = originalFilename.lastIndexOf('.');
        String extension = originalFilename.substring(index - 1);
        //Use UUID to generate file names and retain suffixes
        String fileName = UUID.randomUUID().toString() + extension;

        try {
            //Upload using qiniu cloud component
            QiniuUtils.upload2Qiniu(imgFile.getBytes(), fileName);
            //Store picture name to Redis
            jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_RESOURCES, fileName);
            return new Result(true,MessageConstant.PIC_UPLOAD_SUCCESS,fileName);
        } catch (IOException e) {
            e.printStackTrace();
            return new Result(false,MessageConstant.PIC_UPLOAD_FAIL);
        }
    }

    //Add package
    @RequestMapping("/add")
    public Result add (@RequestBody Setmeal setmeal, Integer[] checkGroupIds) {
        try {
            setMealService.add(setmeal, checkGroupIds);
            return new Result(true, MessageConstant.ADD_SETMEAL_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.ADD_SETMEAL_FAIL);
        }
    }

    //Edit Package
    @RequestMapping("/edit")
    public Result edit (@RequestBody Setmeal setmeal, Integer[] checkGroupIds) {
        try {
            setMealService.edit(setmeal, checkGroupIds);
            return new Result(true, MessageConstant.EDIT_SETMEAL_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.EDIT_SETMEAL_FAIL);
        }
    }

    //Delete package
    @RequestMapping("/delete")
    public Result delete(Integer setMealId) {
        try {
            setMealService.delete(setMealId);
            return new Result(true, MessageConstant.DELETE_SETMEAL_SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.DELETE_SETMEAL_FAIL);
        }
    }

    //Package paging query
    @RequestMapping("/findPage")
    public PageResult findPage(@RequestBody QueryPageBean queryPageBean) {
        return setMealService.findPage(queryPageBean);
    }

    //Query package by Id
    @RequestMapping("/findById")
    public Result findById(Integer setMealId) {
        try {
            Setmeal setmeal = setMealService.findById(setMealId);
            return new Result(true, MessageConstant.QUERY_SETMEAL_SUCCESS, setmeal);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.QUERY_SETMEAL_FAIL);
        }
    }

    //Query relation table by id
    @RequestMapping("/findRelationship")
    public Result findRelationship(Integer setMealId) {
        try {
            List<Integer> checkGroupList = setMealService.findRelationship(setMealId);
            return new Result(true, MessageConstant.QUERY_CHECKGROUP_SUCCESS, checkGroupList);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.QUERY_CHECKGROUP_FAIL);
        }
    }
}

Business layer CRUD

The garbage pictures are processed by Quartz, and the CRUD logic of the pictures stored in the server needs to be processed manually in the business layer, so the business layer involves three parts

  1. Form processing
  2. Redis collection processing
  3. Server image processing

The complete code is given below

package com.cui.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.cui.constant.RedisConstant;
import com.cui.dao.SetMealDao;
import com.cui.entity.PageResult;
import com.cui.entity.QueryPageBean;
import com.cui.pojo.Setmeal;
import com.cui.service.SetMealService;
import com.cui.utils.QiniuUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

@Service(interfaceClass = com.cui.service.SetMealService.class)
@Transactional
public class SetMealServiceImpl implements SetMealService {

    @Autowired
    private SetMealDao setMealDao;

    @Autowired
    private JedisPool jedisPool;

    //New business
    @Override
    public void add(Setmeal setmeal, Integer[] checkGroupIds) {
        //Update package table
        setMealDao.add(setmeal);

        //Update relation table
        for (Integer checkGroupId : checkGroupIds) {
            setMealDao.addRelationShip(setmeal.getId(), checkGroupId);
        }

        //Update Redis
        if (setmeal.getImg() != null)
            jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES, setmeal.getImg());
    }

    //Editing business
    @Override
    public void edit(Setmeal setmeal, Integer[] checkGroupIds) {
        //Be sure to query the old pictures according to the id before updating
        String preImg = setMealDao.getImgById(setmeal.getId());

        //Update package table
        setMealDao.update(setmeal);

        //Update relation table
        //Delete old relationship
        setMealDao.delRelationship(setmeal.getId());
        //Add new relationships
        for (Integer checkGroupId : checkGroupIds) {
            setMealDao.addRelationShip(setmeal.getId(), checkGroupId);
        }

        //Update Redis
        Jedis resource = jedisPool.getResource();
        String newImg = setmeal.getImg();
        //Replace with new picture
        //Delete content in Redis
        resource.srem(RedisConstant.SETMEAL_PIC_DB_RESOURCES, preImg);
        //Delete the content in qiniu ECS
        //Here, garbage cleaning jobs may not be deleted, so write logic to delete them separately, because garbage cleaning will update the records of uploaded pictures
        QiniuUtils.deleteFileFromQiniu(preImg);
        resource.sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES, newImg);
    }

    @Override
    public void delete(Integer setMealId) {
        //Delete Redis cache
        String img = setMealDao.getImgById(setMealId);
        jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_DB_RESOURCES, img);
        //Delete the content in qiniu ECS
        QiniuUtils.deleteFileFromQiniu(img);
        //Delete relation table
        setMealDao.delRelationship(setMealId);
        //Delete package form
        setMealDao.delete(setMealId);
    }

    @Override
    public PageResult findPage(QueryPageBean queryPageBean) {
        String queryString = queryPageBean.getQueryString();
        Integer currentPage = queryPageBean.getCurrentPage();
        Integer pageSize = queryPageBean.getPageSize();

        PageHelper.startPage(currentPage, pageSize);

        Page<Setmeal> page = setMealDao.findPage(queryString);
        return new PageResult(page.getTotal(), page.getResult());
    }

    @Override
    public Setmeal findById(Integer setMealId) {
        return setMealDao.findById(setMealId);
    }

    @Override
    public List<Integer> findRelationship(Integer setMealId) {
        return setMealDao.findRelationship(setMealId);
    }

}

Persistence layer CRUD

Both the server drawing bed and Redis collection are delivered to the business layer for processing. At the persistence layer, there is only simple form processing. The code is given as follows

/**
 * Package service interface
 */
public interface SetMealService {
    void add (Setmeal setmeal, Integer[] checkGroupIds);
    PageResult findPage(QueryPageBean queryPageBean);
    Setmeal findById(Integer setMealId);
    List<Integer> findRelationship(Integer setMealId);
    void edit(Setmeal setmeal, Integer[] checkGroupIds);
    void delete(Integer setMealId);
}

Corresponding mapping profile

<?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.cui.dao.SetMealDao">
    <insert id="add" parameterType="com.cui.pojo.Setmeal">
        <selectKey resultType="Integer" order="AFTER" keyProperty="id">
            select LAST_INSERT_ID()
        </selectKey>
        insert into t_setmeal
        (name, code, helpCode, sex, age, price, remark, attention, img)
        values
        (#{name}, #{code}, #{helpCode}, #{sex}, #{age}, #{price}, #{remark}, #{attention}, #{img})
    </insert>

    <insert id="addRelationShip">
        insert into t_setmeal_checkgroup
        (setmeal_id, checkgroup_id)
        values
        (#{setMealId}, #{checkGroupId})
    </insert>

    <select id="findPage" parameterType="String" resultType="com.cui.pojo.Setmeal">
        select * from t_setmeal
        <if test="value != null and value.length > 0">
            where code = #{value} or name = #{value}
        </if>
    </select>

    <select id="findById" parameterType="Integer" resultType="com.cui.pojo.Setmeal">
        select * from t_setmeal where id = #{value}
    </select>

    <select id="findRelationship" parameterType="Integer" resultType="Integer">
        select checkgroup_id from t_setmeal_checkgroup where setmeal_id = #{value}
    </select>

    <select id="delRelationship" parameterType="Integer">
        delete from t_setmeal_checkgroup where setmeal_id = #{value}
    </select>

    <update id="update" parameterType="com.cui.pojo.Setmeal">
        update t_setmeal
        set
            name = #{name},
            code = #{code},
            helpCode = #{helpCode},
            sex = #{sex},
            age = #{age},
            price = #{price},
            remark = #{remark},
            attention = #{attention},
            img = #{img}
        where
            id = #{id}
    </update>

    <select id="getImgById" parameterType="Integer" resultType="String">
        select img from t_setmeal where id = #{value}
    </select>

    <delete id="delete" parameterType="Integer">
        delete from t_setmeal where id = #{value}
    </delete>
</mapper>

Quartz timing processing

Quartz is a task scheduling framework, which can be used for timing processing

A new module called health is created for timing processing_ Jobs, a web app file packaged in war mode, first on the web Load Spring container in XML

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!-- load spring container -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

ApplicationContext Redis configuring Redis

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxTotal">
                <value>200</value>
            </property>
            <property name="maxIdle">
                <value>50</value>
            </property>
            <property name="testOnBorrow" value="true"/>
            <property name="testOnReturn" value="true"/>
        </bean>
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
            <constructor-arg name="poolConfig" ref="jedisPoolConfig" />
            <constructor-arg name="host" value="127.0.0.1" />
            <constructor-arg name="port" value="6379" type="int" />
            <constructor-arg name="timeout" value="30000" type="int" />
        </bean>

</beans>

Configure the Quartz plug-in for applicationcentext jobs (the plug-in has done dependency injection in Maven)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 					http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <!--Register custom job-->
    <bean id="clearImgJob" class="com.cui.jobs.ClearImgJob"/>

    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- Inject target object -->
        <property name="targetObject" ref="clearImgJob"/>
        <!-- Injection target method -->
        <property name="targetMethod" value="clearImg"/>
    </bean>

    <!-- Register a trigger and specify the time when the task is triggered -->
    <bean id="myTrigger"
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!-- injection JobDetail -->
        <property name="jobDetail" ref="jobDetail"/>
        <!-- Specifies the time to trigger, based on Cron expression -->
        <property name="cronExpression">
            <!--0 0 2 * * ?-->
            <!--Every 10 s Clean up junk files once-->
            <value>0/10 * * * * ?</value>
        </property>
    </bean>

    <!-- Register a unified scheduling factory to schedule tasks through this scheduling factory -->
    <bean id="scheduler"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- Inject multiple triggers -->
        <property name="triggers">
            <list>
                <ref bean="myTrigger"/>
            </list>
        </property>
    </bean>

</beans>

Here we focus on four items: customized job, target object, target method and timing time

Custom job: indicates which class is our custom job

<!--Register custom job-->
<bean id="clearImgJob" class="com.cui.jobs.ClearImgJob"/>

Target object and target method: one is to generate the target object by registering the bean of the custom job, and the other is to indicate which target method we need to obtain the bean of jobDetail

class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- Inject target object -->
        <property name="targetObject" ref="clearImgJob"/>
        <!-- Injection target method -->
        <pproperty name="targetMethod" value="clearImg"/>
    </bean>

The third is the trigger, which defines the trigger time of the task through the corresponding statement, and the timed statement has the corresponding statement generation tool

    <!-- Register a trigger and specify the time when the task is triggered -->
    <bean id="myTrigger"
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!-- injection JobDetail -->
        <property name="jobDetail" ref="jobDetail"/>
        <!-- Specifies the time to trigger, based on Cron expression -->
        <property name="cronExpression">
            <!--0 0 2 * * ?-->
            <!--Every 10 s Clean up junk files once-->
            <value>0/10 * * * * ?</value>
        </property>
    </bean>

Finally, there is a unified task scheduling factory, which can inject multiple triggers to execute multiple tasks

    <!-- Register a unified scheduling factory to schedule tasks through this scheduling factory -->
    <bean id="scheduler"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- Inject multiple triggers -->
        <property name="triggers">
            <list>
                <ref bean="myTrigger"/>
            </list>
        </property>
    </bean>

The last is the custom garbage disposal task

package com.cui.jobs;

import com.cui.constant.RedisConstant;
import com.cui.utils.QiniuUtils;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.JedisPool;

import java.util.Set;

/**
 * Custom job to remove garbage pictures
 */
public class ClearImgJob {

    @Autowired
    private JedisPool jedisPool;

    public void clearImg() {
        //Calculate the difference according to the set set in Redis
        Set<String> trash = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES, RedisConstant.SETMEAL_PIC_DB_RESOURCES);

        if (trash != null) {
            for (String trashName : trash) {
                //Delete the picture on qiniu ECS
                QiniuUtils.deleteFileFromQiniu(trashName);
                //Update Redis
                jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES, trashName);
                System.out.println(trashName + "Cleaned up");
            }
        }

    }
}

Added by downfall on Wed, 02 Feb 2022 14:45:45 +0200