开发项目合集

一、Day01-开发环境搭建

1、实体类分析

image-20250508101039832

2、Git仓库

commit =》push

3、Swagger-Controller层

 /**
  * 员工管理
  */
 @RestController
 @RequestMapping("/admin/employee")
 @Slf4j
 @Api(tags = "员工操作相关接口")
 public class EmployeeController {
 ​
     @Autowired
     private EmployeeService employeeService;
     @Autowired
     private JwtProperties jwtProperties;
 ​
     /**
      * 登录
      *
      * @param employeeLoginDTO
      * @return
      */
     @PostMapping("/login")
     @ApiOperation(value = "员工登录")
     public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
         log.info("员工登录:{}", employeeLoginDTO);
 ​
         Employee employee = employeeService.login(employeeLoginDTO);
 ​
         //登录成功后,生成jwt令牌
         Map<String, Object> claims = new HashMap<>();
         claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
         String token = JwtUtil.createJWT(
                 jwtProperties.getAdminSecretKey(),
                 jwtProperties.getAdminTtl(),
                 claims);
 ​
         EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                 .id(employee.getId())
                 .userName(employee.getUsername())
                 .name(employee.getName())
                 .token(token)
                 .build();
 ​
         return Result.success(employeeLoginVO);
     }
 ​
     /**
      * 退出
      *
      * @return
      */
     @PostMapping("/logout")
     @ApiOperation(value = "员工退出")
     public Result<String> logout() {
         return Result.success();
     }
 ​
     /**
      * 新增员工
      * @param employeeDTO
      * @return
      */
 //因为接收的是JSON格式,所以需要使用@RequestBody,将数据转换为Java对象,并完成数据绑定
     @PostMapping//需要进行的是POST请求
     @ApiOperation(value = "新增员工")
     public Result save(@RequestBody EmployeeDTO employeeDTO) {
         log.info("新增员工{}", employeeDTO);//自动将DTO的属性,补齐至花括号中,占位符
         employeeService.save(employeeDTO);
         return Result.success();
     }
 ​
     /**
      *  分页查询
      *  此处不是JSON数据,而是Query,所以不需要@RequestBody注解来转换
      * @param employeePageQueryDTO
      * @return
      */
     @GetMapping("/page")
     @ApiOperation(value = "员工分页查询")
     public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {
         log.info("员工分页查询,参数为{}", employeePageQueryDTO);
 //        controller层调用service层实现接口
         PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
         //返回结果
         return Result.success(pageResult);
     }
 ​
     /**
      *
      * @param status
      * @param id
      * @return
      */
     @PostMapping("/status/{status}")
     @ApiOperation("启用禁用账号")
     //这里需要解析路径参数 外加@PathVariable
     public Result startOrStop(@PathVariable Integer status,long id){
         //提示信息
         log.info("启用禁用员工账号:{},{}",status,id);
         employeeService.startOrStop(status,id);
         //返回无参构造方法
         return Result.success();
     }
 }

4、config

 /**
  * 配置类,注册web层相关组件
  */
 @Configuration
 @Slf4j
 public class WebMvcConfiguration extends WebMvcConfigurationSupport {
 ​
     @Autowired
     private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
 ​
     /**
      * 注册自定义拦截器
      *
      * @param registry
      */
     protected void addInterceptors(InterceptorRegistry registry) {
         log.info("开始注册自定义拦截器...");
         registry.addInterceptor(jwtTokenAdminInterceptor)
                 .addPathPatterns("/admin/**")
                 .excludePathPatterns("/admin/employee/login");
     }
 ​
     /**
      * 通过knife4j生成接口文档
      * @return
      */
     @Bean
     public Docket docket() {
         ApiInfo apiInfo = new ApiInfoBuilder()
                 .title("苍穹外卖项目接口文档")
                 .version("2.0")
                 .description("苍穹外卖项目接口文档")
                 .build();
         Docket docket = new Docket(DocumentationType.SWAGGER_2)
                 .apiInfo(apiInfo)
                 .select()
                 //指定生成接口需要扫描的包
                 .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
                 .paths(PathSelectors.any())
                 .build();
         return docket;
     }
 ​
     /**
      * 设置静态资源映射
      * @param registry
      */
     protected void addResourceHandlers(ResourceHandlerRegistry registry) {
         log.info("开始设置静态资源映射...");
         registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
         registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
     }
     /**
      * 扩展Spring MVC框架的消息转化器
      */
     protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
         log.info("扩展消息转换器...");
         //创建一个消息转换器对象
         MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
         //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为Json数据
         converter.setObjectMapper(new JacksonObjectMapper());
         //将自己的消息转换器加入容器中
         converters.add(0,converter);
     }
 }

5、处理sql异常-拦截器

 /**
  * 处理SQL异常
  */
 @ExceptionHandler
 public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
     //Duplicate entry 'zhangsan' for key 'employee.idx_username'
     String message = ex.getMessage();
     if(message.contains("Duplicate entry")){
         String[] split = message.split(" ");
         String username = split[2];
         String msg = username + MessageConstant.ALREADY_EXISTS;
         return Result.error(msg);
     }else {
         return Result.error(MessageConstant.UNKNOWN_ERROR);
     }
 }

6、更新数据所携带的id

 log.info("当前员工id:", empId);
 //JWT校验完成登录操作,已经存储了用户的相关信息,其中包括了id,那么我们后续所有修改就是基于这个id
 BaseContext.setCurrentId(empId);

二、Day02-功能实现

1、根据用户名查询员工

 /**
  * 根据用户名查询员工
  * @param username
  * @return
  */
 @Select("select * from employee where username = #{username}")
 Employee getByUsername(String username);

2、员工登录

 /**
  * 员工登录
  * @param employeeLoginDTO
  * @return
  */
 Employee login(EmployeeLoginDTO employeeLoginDTO);
 /**
  * 员工登录
  *
  * @param employeeLoginDTO
  * @return
  */
 public Employee login(EmployeeLoginDTO employeeLoginDTO) {
     String username = employeeLoginDTO.getUsername();
     String password = employeeLoginDTO.getPassword();
 ​
     //1、根据用户名查询数据库中的数据
     Employee employee = employeeMapper.getByUsername(username);
 ​
     //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
     if (employee == null) {
         //账号不存在
         throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
     }
 ​
     //密码比对
     // TODO 后期需要进行md5加密,然后再进行比对(已完成,利用DigestUtils)
     password = DigestUtils.md5DigestAsHex(password.getBytes());
     if (!password.equals(employee.getPassword())) {
         //密码错误
         throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
     }
 ​
     if (employee.getStatus() == StatusConstant.DISABLE) {
         //账号被锁定
         throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
     }
 ​
     //3、返回实体对象
     return employee;
 }

3、插入员工数据

 /**
  * 插入员工数据
  */
 @Insert("insert into employee (name,username,password,phone,sex,id_number,create_time,update_time,create_user,update_user)" +
         "values" +
         "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser})")
 void insert(Employee employee);
/**
 * 新增员工
 * @param employeeDTO
 */
void save(EmployeeDTO employeeDTO);
/**
     * 新增员工
     *
     * @param employeeDTO
     */
    @Override
    public void save(EmployeeDTO employeeDTO) {
        //搞清楚谁包含谁的关系,是Employee包含EmployeeDTO的属性,所以需要进行copy操作
        Employee employee = new Employee();
        //对象属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);
//        设置账号的状态,默认正常状态 1表示正常,0表示锁定
        employee.setStatus(StatusConstant.ENABLE);
//        设置账号的密码,默认为123456,但是此处需要使用md5加密技术,同时使用DigestUtils工具类
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
//        设置当前记录的创建时间
        employee.setCreateTime(LocalDateTime.now());
//        设置当前记录的修改时间
        employee.setUpdateTime(LocalDateTime.now());
//        TODO 后期这里需要改成动态的ID(已完成,利用BaseContext类)
//        设置创建人id
        employee.setCreateUser(BaseContext.getCurrentId());
//        设置修改人id
        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.insert(employee);
    }

4、分页查询

/**
 * 分页查询
 * @param employeePageQueryDTO
 * @return
 */
Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
/**
 * 分页查询
 * @param employeePageQueryDTO
 * @return
 */
PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
/**
 * 分页查询
 *
 * @param employeePageQueryDTO
 * @return
 */
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
    // 传统做法:select * from employee limit 0,10
    // 此处利用插件,pageHelper直接进行分页查询,获取当前页面,再获取页面大小
    PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
    //调用Mapper,访问数据层,执行sql语句
    Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
    //以上所得为Page对象,我们所需要的是PageResult对象,所以需要获取
    long total = page.getTotal();
    List<Employee> records = page.getResult();
    return new PageResult(total, records);
}

5、启动禁用账号

/**
 * 启动禁用账号
 * @param employee
 */
void update(Employee employee);
/**
 * 启用禁用用户账号
 * @param status
 * @param id
 */
void startOrStop(Integer status, long id);
/**
 * 启用禁用员工账号
 * @param status
 * @param id
 */
@Override
public void startOrStop(Integer status, long id) {
    //一般为update employee set status = ? where id = ?
    Employee employee = new Employee();
    employee.setId(id);
    employee.setStatus(status);
    employeeMapper.update(employee);
}

6、EmployeeMapper.xml

<mapper namespace="com.sky.mapper.EmployeeMapper">
    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
        <where>
            <if test="name != null and name != ''">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
    <update id="update">
        update employee
        <set>
            <if test="name != null">
                name = #{name},
            </if>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="password != null">
                password = #{password},
            </if>
            <if test="phone != null">
                phone = #{phone},
            </if>
            <if test="sex != null">
                sex = #{sex},
            </if>
            <if test="idNumber != null">
                id_number = #{idNumber},
            </if>
            <if test="updateTime != null">
                update_Time = #{updateTime},
            </if>
            <if test="updateUser != null">
                update_User = #{updateUser},
            </if>
            <if test="status != null">
                status = #{status},
            </if>
        </set>
        where id = #{id}
    </update>
</mapper>

路径参数要加注解@PathVariable,可以用于取路径参数,结合花括号占位符,{}

7、根据ID查询用户信息

image-20250508144503725

1. Controller层,直接接收前端的HTTP请求

  • 注意返回对象的要求

  • 注意请求类型、请求地址

  • 注意返回形式是否为JSON数据

/**
 *  根据id查询员工
 * @param id
 * @return
 */
@GetMapping("/{id}")
@ApiOperation("根据ID查询员工")
public Result<Employee> getById(@PathVariable long id) {
    Employee employee = employeeService.getById(id);
    return Result.success(employee);
}

2. Service层的抽象接口

/**
 * 根据id查询用户信息
 * @param id
 * @return
 */
Employee getById(long id);

3. Service层的接口实现类

/**
 * 根据id查询用户信息
 * @param id
 * @return
 */
@Override
public Employee getById(long id) {
    Employee employee = employeeMapper.getById(id);
    employee.setPassword("****");
    return employee;
}

4. Mapper层与数据交互

  • 如若是简单的sql语句可以直接注解编写

  • 若是动态sql,则需要在xml文件中编写

/**
 * 根据id查询用户信息
 * @return
 */
@Select("select * from employee where id = #{id}")
Employee getById(long id);

8、编辑员工信息

这里为什么只需要employeeDTO对象:

  1. 因为我们所需要的用户信息包含在此对象内

  2. 可以通过BeanUtils的工具类将获取到的employeeDTO属性复制到employee中

    1. 然后在employeeDTO中没有的属性,在employee中另行添加即可

    2. 这样可防止最开始直接使用employee对象,但是很多没必要的值被修改

/**
 * 编辑用户信息
 * @param employeeDTO
 * @return
 */
@PutMapping
@ApiOperation("编辑员工信息")
public Result update(@RequestBody EmployeeDTO employeeDTO) {
    log.info("编辑员工信息{}", employeeDTO);//Swagger接口的信息提示
    employeeService.update(employeeDTO);
    return Result.success();
}
/**
 * 编辑用户信息
 * @param employeeDTO
 */
void update(EmployeeDTO employeeDTO);
/**
 * 编辑用户信息
 * @param employeeDTO
 */
@Override
public void update(EmployeeDTO employeeDTO) {
    Employee employee = new Employee();
    BeanUtils.copyProperties(employeeDTO,employee);
    employee.setUpdateTime(LocalDateTime.now());
    employee.setUpdateUser(BaseContext.getCurrentId());
    employeeMapper.update(employee);
}
/**
 * 启动禁用账号、更新账户信息【复用】
 * @param employee
 */
void update(Employee employee);
<update id="update">
    update employee
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="password != null">
            password = #{password},
        </if>
        <if test="phone != null">
            phone = #{phone},
        </if>
        <if test="sex != null">
            sex = #{sex},
        </if>
        <if test="idNumber != null">
            id_number = #{idNumber},
        </if>
        <if test="updateTime != null">
            update_Time = #{updateTime},
        </if>
        <if test="updateUser != null">
            update_User = #{updateUser},
        </if>
        <if test="status != null">
            status = #{status},
        </if>
    </set>
    where id = #{id}
</update>

9、导入分类管理功能【略过】

三、Day03-功能实现

1、公共字段自动填充

image-20250508152554569

image-20250509212153159

四、剪不断理还乱

1、entity【此部分是对应数据库中字段】

2、dto【此部分用于前后端交互】

@Data
public class DishDTO implements Serializable {

    private Long id;
    //菜品名称
    private String name;
    //菜品分类id
    private Long categoryId;
    //菜品价格
    private BigDecimal price;
    //图片
    private String image;
    //描述信息
    private String description;
    //0 停售 1 起售
    private Integer status;
    //口味
    private List<DishFlavor> flavors = new ArrayList<>();

}
@Data
public class CategoryPageQueryDTO implements Serializable {

    //页码
    private int page;

    //每页记录数
    private int pageSize;

    //分类名称
    private String name;

    //分类类型 1菜品分类  2套餐分类
    private Integer type;

}

3、vo【向前端进行页面展示】

4、Controller【接收处理前端发起的HTTP请求】

/**
 * 菜品分页查询
 * @param dishPageQueryDTO
 * @return
 */
@GetMapping("/page")
@ApiOperation("菜品分页查询")
//最后得到的结果是一个集合,所以泛型返回需要的是一个集合对象,然后传递的参数是query类型,即key value形式,所以不需要@RequestBody
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO){
    log.info("菜品分页查询:{}",dishPageQueryDTO);
    PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
    return Result.success(pageResult);
}

首先需要知道的是:

  1. 使用的是什么Method

    1. Get

    2. Post

  2. 接口的设计,ApiOperation

  3. 返回类型的设定,是否需要返回值,看最后是否获取到数据,如果是个集合,那就要注意了,如果只是插入或者删除操作,则不需要返回值

  4. 传入的参数是否是地址参数,如果是地址参数,则需要@Requestbody,如果是普通的query参数,key、value形式

  5. 调用Service,在service中涉及具体的操作,实际处理业务

  6. 因为调用,所以需要自动注解dishService,使用其具体方法

  7. 最后返回Result结果给前端展示,success

5、Service【与Controller层进行交互,处理数据业务操作】

/**
 * 菜品分页查询,真正的实现操作
 * @param dishPageQueryDTO
 * @return
 */
@Override
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
    //分页操作的真正实现,此处使用pageHelper插件
    PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
    //把剩余的参数传递
    Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
    //得到的list集合封装到PageResult中,total是总数量,Result是所有的返回结果
    return new PageResult(page.getTotal(),page.getResult());
}

此处的分页操作

  1. 使用的是PageHelper插件

  2. 在service中操作,需要实际用到Mapper,所以需要在此处导入dishMapper

  3. 此处的方法被controller层使用,然后在此处,需要mapper实际与数据库进行交互

  4. 注意要返回的对象类型

6、Mapper【与数据库的数据进行交互】

/**
 * 菜品分页查询
 * @param dishPageQueryDTO
 * @return
 */
Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);

写入Mapper操作,此处如果是静态sql,只需要在上面写上注解

一个Mapper文件对应一个sql操作

如果是动态sql,如下:

<select id="pageQuery" 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>

7、AutoFill,注解【公共字段】

/**
 * 自定义注解,用于标识某个方法需要进行字段自动填充
 */
@Target(ElementType.METHOD)//指定注解只能加载在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//    数据库操作类型:Update、Insert
    OperationType value();
}

  • URL 或表单参数@RequestParam

  • JSON/XML 请求体@RequestBody

  • HTTP 请求头参数@RequestHeader

  • 定义接口路由@RequestMapping

@RequestParam 可以省略的情况

条件:

  • 方法参数名和请求参数名 完全一致

  • 请求是 GET 或表单 POST 类型。

  • 参数是 非必须(即不是 required=true)有默认值

❌ 不能省略的情况(@RequestParam)

  • 参数名不一致时必须用 @RequestParam("paramName")

  • 想设置默认值或是否必须时也必须加注解

@Transactional

@Transactional 是 Spring 中用于 声明式事务管理 的注解。它可以确保一段代码在数据库操作时 要么全部成功、要么全部失败(原子性),常用于涉及多个数据库操作的方法上。

DTO、ENTITY、VO的三者区别

场景用什么
接收前端表单/请求参数DTO(参数校验、字段精简)
与数据库交互Entity(ORM映射)
返回前端展示用数据VO(展示友好、数据组合)

五、Redis入门

image-20250516171226632

image-20250516171215629

image-20250516171620330

image-20250516171901744

image-20250516172444038

image-20250516173218156

1. 编写Yml

redis:
  host: ${sky.redis.host}
  port: ${sky.redis.port}
  password: ${sky.redis.password}
redis:
  host: localhost
  port: 6379
  password: 123456

2. 编写配置文件

@Configuration
@Slf4j
public class RedisConfiguration {
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建redis模板对象...");
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

3. 编写测试文件

@SpringBootTest
public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate() {
        System.out.println(redisTemplate);
        //Redis常用操作几种数据类型
        ValueOperations valueOperations = redisTemplate.opsForValue();//【String】
        HashOperations hashOperations = redisTemplate.opsForHash();//【Hash】
        ListOperations listOperations = redisTemplate.opsForList();//【List】
        SetOperations setOperations = redisTemplate.opsForSet();//【Set】
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();//【Zset】
    }
}

4. 测试类集合

@SpringBootTest
public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testRedisTemplate() {
        System.out.println(redisTemplate);
        //Redis常用操作几种数据类型
        ValueOperations valueOperations = redisTemplate.opsForValue();//【String】
        HashOperations hashOperations = redisTemplate.opsForHash();//【Hash】
        ListOperations listOperations = redisTemplate.opsForList();//【List】
        SetOperations setOperations = redisTemplate.opsForSet();//【Set】
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();//【Zset】
    }

    @Test
    public void testString() {
        //set get setex setnx
        redisTemplate.opsForValue().set("name", "xiaoming");
        String name = (String) redisTemplate.opsForValue().get("name");
        System.out.println(name);
        redisTemplate.opsForValue().set("code", "123", 3, TimeUnit.MINUTES);//设置过期时间
        redisTemplate.opsForValue().setIfAbsent("lock", "1");
        redisTemplate.opsForValue().setIfAbsent("lock", "2");
    }

    @Test
    public void testHash() {
        //hset hget hdel hkeys hvals
//        HashOperations hashOperations = redisTemplate.opsForHash();
        redisTemplate.opsForHash().put("hashlist", "name", "mao");
        redisTemplate.opsForHash().put("hashlist", "age", "24");

        String name = (String) redisTemplate.opsForHash().get("hashlist", "name");
        String age = (String) redisTemplate.opsForHash().get("hashlist", "age");

        System.out.println("name:" + name);
        System.out.println("age:" + age);

        System.out.println("有:" + redisTemplate.opsForHash().keys("hashlist"));

        System.out.println("删除前:" + redisTemplate.opsForHash().values("hashlist"));

        redisTemplate.opsForHash().delete("hashlist", "age");

        System.out.println("删除后:" + redisTemplate.opsForHash().values("hashlist"));
    }

    @Test
    public void testList() {
        //lpush lrange lpop llen
        ListOperations listOperations = redisTemplate.opsForList();
        listOperations.leftPush("list", "a");//一次一个
        listOperations.leftPushAll("list", "b", "c", "d");//不限制

        System.out.println("删除前:" + listOperations.range("list", 0, -1));
        System.out.println("删除前:" + listOperations.size("list"));

        listOperations.rightPop("list");
        listOperations.leftPop("list");

        System.out.println("删除后:" + listOperations.range("list", 0, -1));
        System.out.println("删除后:" + listOperations.size("list"));

    }

    @Test
    public void testSet() {
        SetOperations setOperations = redisTemplate.opsForSet();
        setOperations.add("set1", "a", "b");
        setOperations.add("set2", "b", "c", "d");
        System.out.println(setOperations.size("set1"));
        System.out.println(setOperations.size("set2"));
        System.out.println("返回成员:" + setOperations.members("set1"));
        System.out.println("返回成员:" + setOperations.members("set2"));
        System.out.println("交集:" + setOperations.intersect("set1", "set2"));
        System.out.println("并集:" + setOperations.union("set1", "set2"));
        setOperations.remove("set1", "a");
        System.out.println("删除后返回成员:" + setOperations.members("set1"));
        System.out.println(setOperations.size("set1"));
        System.out.println(setOperations.size("set2"));
    }

    @Test
    public void testZSet() {
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add("zset", "mao", 10.8);
        zSetOperations.add("zset", "chen", 10.5);
        zSetOperations.add("zset", "zhu", 10.3);

        System.out.println("开始:" + zSetOperations.range("zset", 0, -1));

        zSetOperations.incrementScore("zset", "zhu", 5);

        System.out.println("结束:" + zSetOperations.range("zset", 0, -1));

        zSetOperations.remove("zset", "zhu");

        System.out.println("最后:" + zSetOperations.range("zset", 0, -1));
    }

    @Test
    public void testCommon() {
        //keys exists type del
        Set keys = redisTemplate.keys("*");
        System.out.println(keys);

        Boolean set = redisTemplate.hasKey("set");
        System.out.println(redisTemplate.hasKey("set1"));

        for (Object key: keys){
            DataType type = redisTemplate.type(key);
            System.out.println(type.name());
        }
        redisTemplate.delete("set1");
    }
}

5. 店铺营业状态设置

package com.sky.controller.admin;

import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Slf4j
@Api("店铺相关接口")
public class ShopController {

    public static final String KEY = "SHOP_STATUS";

    @Autowired
    private RedisTemplate redisTemplate;

    //1. 更改管理端营业状态
    //2. 获取管理端营业状态
    //3. 获取用户端营业状态

    /**
     * 设置店铺营业状态
     *
     * @param status
     * @return
     */
    @PutMapping("/{status}")
    @ApiOperation("设置店铺营业状态")
    public Result setStatus(@PathVariable Integer status) {
        log.info("设置店铺营业状态: {}", status == 1 ? "营业中" : "打烊中");
        redisTemplate.opsForValue().set(KEY, status);
        return Result.success();
    }

    @GetMapping("/status")
    @ApiOperation("查询店铺营业状态")
    public Result<Integer> getStatus() {
        Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
        log.info("店铺营业状态为:{}", status == 1 ? "营业中" : "打烊中");
        return Result.success(status);
    }

}
package com.sky.controller.user;

import com.sky.result.Result;
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.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

@RestController("userShopController")
@RequestMapping("/user/shop")
@Slf4j
@Api("店铺相关接口")
public class ShopController {

    public static final String KEY = "SHOP_STATUS";

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/status")
    @ApiOperation("查询店铺营业状态")
    public Result<Integer> getStatus() {
        Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
        log.info("店铺营业状态为:{}", status == 1 ? "营业中" : "打烊中");
        return Result.success(status);
    }
}

六、SpringCache入门

image-20250522172347140

image-20250522173011057

useGeneratedKeyskeyProperty 属性详解

这两个属性是 MyBatis 中用于处理数据库自动生成主键(如自增ID)的重要配置,通常一起使用在 <insert> 标签中。

parameterType="User" 是 MyBatis 映射文件(XML)中的一个属性,用于指定 SQL 语句接收的参数类型。它的主要作用是告诉 MyBatis 这个 SQL 语句将接收什么类型的参数对象。

在 MyBatis 的 <foreach> 标签中,openclose(或 end,取决于 MyBatis 版本)属性用于控制循环生成的 SQL 片段的前缀和后缀。它们通常在需要动态构建带有特定语法结构的 SQL 时使用。以下是详细说明:


openclose 属性的作用

属性用途示例值
open在循环开始前添加的字符串(前缀)"(", "WHERE "
close在循环结束后添加的字符串(后缀)")", " OR "

你提到的代码 for(ShoppingCart cart : shoppingCartList) 是 Java 的 增强型 for 循环(Enhanced for-loop),其结构可以分解为以下部分:

for(类型 对象名 : 集合/数组) {
    // 循环体
}

graph TD
    A[开始] --> B[初始化当前团 current = 空集,候选点集 candidate = 所有点]
    B --> C{candidate 是否为空?}
    C -- 否 --> D[选一个点 v ∈ candidate]
    D --> E{v 是否与 current 中所有点相连?}
    E -- 是 --> F[将 v 加入 current,更新 candidate 为与 v 相连的候选点]
    F --> C
    E -- 否 --> G[跳过 v]
    G --> C
    C -- 是 --> H{current 是否大于 best?}
    H -- 是 --> I[更新 best = current]
    H -- 否 --> J[回溯返回上一层]
    I --> J

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值