Public field automatic filling, dish management

1. Public field filling

1.1. Problem analysis

1.2. Implementation ideas

1.3, Code Development

1.3.1, Custom annotations

import com.sky.enumeration.OperationType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //Specify the type of database operation through the enumeration type
    //UPDATE INSERT
    OperationType value();
}

1.3.2, Aspect class

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * Customize aspects to implement the logic of automatic filling of public parts
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    /**
     * Entry point: Which methods of those classes are intercepted
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) & amp; & amp; @annotation(com.sky.annotation.AutoFill)") //Point-cut expression
    public void autoFillPointCut(){}
    /**
     * Pre-notification, assign values to public fields in the notification
     */
    @Before ( "autoFillPointCut()" )
    public void autoFill(JoinPoint joinPoint){
        log.info ( "Start automatic filling of public fields" );

        //Get the database operation type on the currently intercepted method (get the signature)
        MethodSignature signature = (MethodSignature) joinPoint.getSignature ();//Get the method signature
        AutoFill autoFill = signature.getMethod ().getAnnotation ( AutoFill.class );//Get the annotation object on the method
        OperationType operationType = autoFill.value ();//Get the database operation type
        //Get the currently intercepted parameters--the entity object
        Object[] args = joinPoint.getArgs ();
        if (args==null || args.length==0){
            return;
        }
        Object entity = args[0];
        //Prepare data for assignment
        LocalDateTime now = LocalDateTime.now ();
        Long currentId = BaseContext.getCurrentId ();

        //According to the current different operation types, assign values to the corresponding properties through reflection
        if (operationType==OperationType.INSERT ){
            //Assign values to four public fields
            try {
                //Get method through reflection
                Method setCreatTime = entity.getClass ().getDeclaredMethod ( AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class );
                Method setCreateUser = entity.getClass ().getDeclaredMethod (AutoFillConstant.SET_CREATE_USER , Long.class );
                Method setUpdateTime = entity.getClass ().getDeclaredMethod ( AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class );
                Method setUpdateUser = entity.getClass ().getDeclaredMethod ( AutoFillConstant.SET_UPDATE_USER, Long.class );
                //Assignment through reflection
                setCreatTime.invoke (entity,now);
                setCreateUser.invoke (entity,currentId);
                setUpdateTime.invoke (entity,now);
                setUpdateUser.invoke (entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if (operationType==OperationType.UPDATE){
            try {
                Method setUpdateTime = entity.getClass ().getDeclaredMethod ( AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class );
                Method setUpdateUser = entity.getClass ().getDeclaredMethod ( AutoFillConstant.SET_UPDATE_USER, Long.class );
                setUpdateTime.invoke (entity,now);
                setUpdateUser.invoke (entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //Assign values to two public fields
        }
    }

}

2. Add new dishes

2.1, Requirements Analysis and Design

2.2. File upload

1.Controller layer

import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

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

/**
 * Common interface
 */
@RestController
@RequestMapping("/admin/common")
@Slf4j
@Api(tags = "Common Interface")
public class CommonController {
    @Autowired
    private AliOssUtil aliOssUtil;
    /**
     * File Upload
     * @param file
     * @return
     */
    @PostMapping("/upload")
@ApiOperation ( "File Upload" )
 public Result<String> upload(MultipartFile file){
    log.info ("File upload,{}",file);
        try {
            String originalFilename = file.getOriginalFilename ();//Get the original file name of the file
            //Intercept the suffix of the original file name
            String extension = originalFilename.substring ( originalFilename.lastIndexOf ( "." ) );
            //Create new file name
            String objectName = UUID.randomUUID ().toString () + extension;
            String filePath = aliOssUtil.upload ( file.getBytes (), objectName );
            return Result.success (filePath);
        } catch (IOException e) {
            log.error ("File upload failed,{}",e);
        }
        return Result.error ( "File upload failed" );

 }



}

2.3. New dishes

1.DishController

 /**
     * Added new dishes
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation ( "Add new dishes" )
    public Result save(@RequestBody DishDTO dishDTO){
        log.info ("New dishes,{}",dishDTO);
        dishService.saveWithFlavor (dishDTO);
        return Result.success();

    }

2.DishServiceImlp

 /**
     * Added new dishes and corresponding flavors
     * @param dishDTO
     */
    @Transactional //Annotation-based transaction management operates multiple tables to ensure data atomicity and consistency.
    @Override
    public void saveWithFlavor(DishDTO dishDTO) {
        Dish dish = new Dish ();
        BeanUtils.copyProperties (dishDTO,dish);
        //Insert a piece of data into the menu table
        dishMapper.insert(dish);
        //Get the primary key value generated by the Insert statement
        Long dishId = dish.getId ();
        //Insert n pieces of data into the taste table
        List<DishFlavor> flavors = dishDTO.getFlavors ();
        if (flavors !=null & amp; & amp; flavors.size () >0){
            flavors.forEach ( dishFlavor -> dishFlavor.setDishId ( dishId ));
            //Insert n pieces of data into the taste table
         dishFlavorMapper.insertBatch(flavors);

        }

3.Mapper

import com.sky.annotation.AutoFill;
import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface DishMapper {

    /**
     * Query the number of dishes based on category id
     * @param categoryId
     * @return
     */
    @Select("select count(id) from dish where category_id = #{categoryId}")
    Integer countByCategoryId(Long categoryId);

    /**
     * Modify dish information
     * @param dishDTO
     */
    void updateDish(DishDTO dishDTO);

    /**
     * Added new dishes
     * @param dish
     */
    @AutoFill(OperationType.INSERT)
    void insert(Dish dish);
}
import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface DishFlavorMapper {
    /**
     * Batch insert flavor data
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);
}

4.XML

 <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish(id,name,category_id,price,image,description,status,create_time,update_time,create_user,update_user)
        values(#{id},#{name},#{categoryId},#{price},#{image},#{description},#{status},#{createTime},#{updateTime},#{createUser },#{updateUser})
    </insert>
 <insert id="insertBatch">
        insert into dish_flavor (dish_id,name,value) values
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>

    </insert>

3. Dishes page query

3.1. Requirements analysis and design

3.2, code implementation

1.Controller

 /**
     *Page query of dishes
     * @param dishPageQueryDTO
     * @return
     */
    @ApiOperation ( "Paging query of dishes" )
    @GetMapping("/page")
    public Result<PageResult> dishPageQuery(DishPageQueryDTO dishPageQueryDTO){
        log.info ("Paging query,{}",dishPageQueryDTO);
    PageResult pageResult= dishService.dishPageQuery(dishPageQueryDTO);
        return Result.success (pageResult);

    }

2.Service

 /**
     *Page query of dishes
     * @param dishPageQueryDTO
     * @return
     */
    @ApiOperation ( "Paging query of dishes" )
    @GetMapping("/page")
    public Result<PageResult> dishPageQuery(DishPageQueryDTO dishPageQueryDTO){
        log.info ("Paging query,{}",dishPageQueryDTO);
    PageResult pageResult= dishService.dishPageQuery(dishPageQueryDTO);
        return Result.success (pageResult);

    }

3.Mapper

<!--Page query-->
    <select id="dishPageQuery" resultType="com.sky.vo.DishVO">
        select d.* ,c.name as categoryName from dish d left outer join category c on d.category_id = c.id
        <where>
            <if test="name !=null"> and d.name like concat('%',#{name},'%')</if>
            <if test="categoryId !=null">and d.category_id =#{categoryId}</if>
            <if test="status !=null"> and d.status=#{status}</if>
        </where>
        order by d.create_time desc
    </select>

4. Delete dishes

4.1. Requirements analysis and design

4.2, code implementation

1.Controller layer

 /**
     * Delete dishes in batches
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation ( "Delete dishes in batches" )
    public Result delete(@RequestParam List<Long> ids ){
        log.info ( "Batch deletion of dishes,{}",ids );
        dishService.deleteBatch(ids);
        return Result.success();

    }

2.Service layer

 /**
     * batch deletion
     * @param ids
     */
    @Transactional
    @Override
    public void deleteBatch(List<Long> ids) {
        //Determine whether the current dish can be deleted - is there a dish that is on sale? ?
        for (Long id : ids) {
        Dish dish=dishMapper.getById(id);
        if (dish.getStatus ()== StatusConstant.ENABLE){
            //The current dish is on sale and cannot be deleted.
            throw new DeletionNotAllowedException (MessageConstant.DISH_ON_SALE);
        }
        }

        //Determine whether the current dish can be deleted--whether the current dish is associated with the set meal
        List<Long> setmealIds = setmealDishMapper.GetSetmealIdsByDishIds ( ids );
        if (setmealIds !=null & amp; & amp; setmealIds.size ()>0){
            //The current dish is associated with the package and cannot be deleted
            throw new DeletionNotAllowedException ( MessageConstant.DISH_BE_RELATED_BY_SETMEAL );
        }

        //Delete the dish data in the dish table
        for (Long id : ids) {
            dishMapper.deleteById(id);
            //Delete the taste data associated with the dish
            dishFlavorMapper.deleteByDishId(id);
        }

        

    }

3.Mapper layer

<?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.sky.mapper.SetmealDishMapper">

<!--Query the package ID based on the dish ID-->
    <select id="GetSetmealIdsByDishIds" resultType="java.lang.Long">
     select setmeal_id from setmeal_dish where dish_id in
    <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
        #{dishId}
    </foreach>

    </select>
</mapper>
import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface DishFlavorMapper {
    /**
     * Batch insert flavor data
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);

    /**
     * Delete the corresponding taste data according to the dish ID
     * @param dishId
     */
    @Delete ( "delete from dish_flavor where dish_id=#{dishId}" )
    void deleteByDishId(Long dishId);
}

5. Modify the dishes

5.1. Requirements analysis and design

5.2. Development of interface for querying dishes based on ID

service layer

 /**
     * Query dish information and taste based on ID
     * @param id
     * @return
     */
    @Transactional
    @Override
    public DishVO getById(Long id) {
    Dish dish =dishMapper.getById (id);
    List<DishFlavor> flavors = dishFlavorMapper.getFlavor(id);
        DishVO dishVO = new DishVO ();
        BeanUtils.copyProperties (dish,dishVO);
        dishVO.setFlavors ( flavors );
        return dishVO;
    }

5.3. Modify dish interface development

1.Controller layer

 /**
     * Modify dish information
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation ( "Modify dishes" )
    public Result updateDish(@RequestBody DishDTO dishDTO){
        log.info ("Modify dishes,{}",dishDTO);
        dishService.updateDish(dishDTO);
        return Result.success();
    }

2.Service layer

 /**
     * Modify dishes
     * @param dishDTO
     */
    @AutoFill (value = OperationType.UPDATE)
    @Override
    public void updateDish(DishDTO dishDTO) {
        //Modify basic information of dishes
        Dish dish = new Dish ();
        BeanUtils.copyProperties (dishDTO,dish);
        dishMapper.updateDish(dish);
        //Delete the original flavor data
        dishFlavorMapper.deleteByDishId (dishDTO.getId () );
        //Reinsert flavor data
        List<DishFlavor> flavors = dishDTO.getFlavors ();
        if (flavors !=null & amp; & amp; flavors.size ()>0){
            flavors.forEach ( dishFlavor ->{
                dishFlavor.setDishId ( dishDTO.getId () );
            } );
            dishFlavorMapper.insertBatch ( flavors );
        }


    }

3.Mapper layer

 <update id="updateDish">
    update dish
    <set>
        <if test="categoryId !=null">category_id=#{categoryId},</if>
        <if test="description !=null">description=#{description},</if>
        <if test="id !=null">id =#{id},</if>
        <if test="image !=null">image =#{image},</if>
        <if test="name !=null">name =#{name},</if>
        <if test="price !=null">price =#{price},</if>
        <if test="status !=null">status=#{status}</if>
    </set>
    where id=#{id}
    </update>
 <insert id="insertBatch">
        insert into dish_flavor (dish_id,name,value) values
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>

    </insert>
 /**
     * Delete the corresponding taste data according to the dish ID
     * @param dishId
     */
    @Delete ( "delete from dish_flavor where dish_id=#{dishId}" )
    void deleteByDishId(Long dishId);