【苍穹外卖 | 篇④】功能实现(三)

在牛某网看见了牛肉哥的帖子之后,打算向牛肉大佬学习,故开始书写优快云博客,通过博客的方式来巩固自身知识学习。

因为之前有粗略的学习了Java Web的基础课程,所以博客内容主要是巩固之前学习当中的模糊点,以及一些自己认为重要的内容,用于自己进一步的掌握开发技能。

内容:

  • 新增套餐

  • 分页查询套餐

  • 删除套餐

  • 修改套餐

  • 启动与停售套餐

新增套餐:

@Override
    @Transactional
    public void saveWithDish(SetmealDTO setmealDTO) {
        Setmeal setmeal = new Setmeal();
        BeanUtils.copyProperties(setmealDTO, setmeal);
        //setmealDTO 中存在但 setmeal 中没有的属性,会被直接忽略,不会对 setmeal 产生任何影响。
        setmealMapper.insert(setmeal);
        Long setmealId = setmeal.getId();
        //1. 初始化 setmeal → id = null
        //2. BeanUtils 拷贝 DTO → id 仍为 null(DTO 里的 setmealId 无效)
        //3. 调用 setmealMapper.insert(setmeal) →
        //   a. 数据库/后端生成有效 id(比如 1001)
        //   b. MyBatis 自动把 1001 回写到 setmeal 的 id 字段
        //4. 执行 Long setmealId = setmeal.getId() → 拿到 1001(有效值)
        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        //由于get到除了setmealid的信息,所以需要遍历进行补足
        setmealDishes.forEach(setmealDish -> {
            setmealDish.setSetmealId(setmealId);
        });
        setmealDishMapper.insertBatch(setmealDTO.getSetmealDishes());

    }
  • setmealDTO 中存在但 setmeal 中没有的属性,会被直接忽略,不会对 setmeal 产生任何影响。
  • 1. 初始化 setmeal → id = null
  • 2. BeanUtils 拷贝 DTO → id 仍为 null(DTO 里的 setmealId 无效)
  • 3. 调用 setmealMapper.insert(setmeal) →
  • a. 数据库/后端生成有效 id(比如 1001)
  • b. MyBatis 自动把 有效id 回写到 setmeal 的 id 字段
  • 4. 执行 Long setmealId = setmeal.getId() → 拿到 有效id(有效值)
  • 由于get到除了setmealid的信息,所以需要遍历进行补足

分页查询套餐:

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

注意点:

<if>:if里要写明是谁的字段:例如:and s.name

删除套餐:

    @DeleteMapping
    @ApiOperation("删除套餐")
    public Result deleteBatch(@RequestParam List<Long> ids) {
        log.info("删除套餐:{}",ids);
        setmealService.deleteBatch(ids);
        return Result.success();
    }

注意点:

请求参数名要保持一致,同时要加上@RequestParam来将string型ids变为List<Long>型。

    @Override
    @Transactional
    public void deleteBatch(List<Long> ids) {
        ids.forEach( id -> {
            Setmeal setmeal = setmealMapper.getById(id);
            if(setmeal.getStatus() == StatusConstant.ENABLE){
                throw new DeletionNotAllowedException(MessageConstant.SETMEAL_ON_SALE);
            }
        });
        //先删除掉setmeal_dish表的数据
        setmealDishMapper.deleteByIds(ids);
        //再删除掉setmeal表里的套餐
        setmealMapper.deleteByIds(ids);
    }

注意点:

删除操作前要记住先检查售卖情况,遍历操作是ids,foreach

修改套餐:

 @Transactional
    @Override
    public void update(SetmealDTO setmealDTO) {
        //先删除掉setmeal_dish里的原套餐内容
        Long setmealId = setmealDTO.getId();
        setmealDishMapper.deleteById(setmealId);
        //再插入进去新的数据
        List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();
        if (setmealDishes != null && setmealDishes.size() > 0){
            setmealDishes.forEach(setmealDish -> {
                setmealDish.setSetmealId(setmealId);
            });
        }
        setmealDishMapper.insertBatch(setmealDishes);
        Setmeal setmeal=new Setmeal();
        BeanUtils.copyProperties(setmealDTO, setmeal);
        //再更新setmeal表里的数据
        setmealMapper.update(setmeal);
    }

启动与停售套餐:

    @Override
    public void startOrStop(Integer status, Long id) {
        //起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
        //通过套餐菜品表联系到菜品表,通过套餐id得到套餐菜品id,从而去菜品表里查status
        List<SetmealDish> setmealDishes =setmealDishMapper.getBySetmealId(id);
        setmealDishes.forEach(setmealDish -> {
            Long dishId=setmealDish.getDishId();
            Dish dish=dishMapper.getById(dishId);
            if(dish.getStatus() == StatusConstant.DISABLE){
                throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
            }
        });
        Setmeal setmeal = Setmeal.builder()
                .id(id)
                .status(status)
                .build();
        setmealMapper.update(setmeal);
    }
 @Transactional
    @Override
    public void startOrStop(Integer status, Long id) {
        //起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"
        if(status == StatusConstant.ENABLE){
            //select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?
            List<Dish> dishList = dishMapper.getBySetmealId(id);
            if(dishList != null && dishList.size() > 0){
                dishList.forEach(dish -> {
                    if(StatusConstant.DISABLE == dish.getStatus()){
                        throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);
                    }
                });
            }
        }

        Setmeal setmeal = Setmeal.builder()
                .id(id)
                .status(status)
                .build();
        setmealMapper.update(setmeal);
    }

注意点:

第一种没有第二种的效果好:

  • 如果套餐内有停售菜品,直接抛出
  • 提前校验能避免无效操作

苍穹外卖登录与注册功能实现通常包含前端和后端两部分,以下为详细的实现方法: ### 注册功能实现 #### 前端部分 前端需创建注册页面,包含用户名、密码、手机号等输入框,以及注册按钮。用户输入信息后,点击注册按钮,前端通过表单提交或异步请求将数据发送至后端。以下是一个简单的 HTML 表单示例: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>苍穹外卖注册</title> </head> <body> <form id="registerForm"> <input type="text" id="username" placeholder="用户名" required> <input type="password" id="password" placeholder="密码" required> <input type="tel" id="phone" placeholder="手机号" required> <button type="submit">注册</button> </form> <script> document.getElementById('registerForm').addEventListener('submit', function (e) { e.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; const phone = document.getElementById('phone').value; // 发送请求到后端 fetch('/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: username, password: password, phone: phone }) }) .then(response => response.json()) .then(data => { if (data.success) { alert('注册成功'); // 跳转到登录页面 window.location.href = '/login'; } else { alert(data.message); } }); }); </script> </body> </html> ``` #### 后端部分 后端接收前端发送的注册信息,进行数据验证,若信息合法,则将用户信息插入数据库。以下是一个使用 Python Flask 框架的示例: ```python from flask import Flask, request, jsonify import sqlite3 app = Flask(__name__) @app.route('/register', methods=['POST']) def register(): data = request.get_json() username = data.get('username') password = data.get('password') phone = data.get('phone') # 简单的数据验证 if not username or not password or not phone: return jsonify({'success': False, 'message': '信息不完整'}), 400 # 连接数据库 conn = sqlite3.connect('sky_take_out.db') cursor = conn.cursor() try: # 插入用户信息 cursor.execute('INSERT INTO user (username, password, phone_number) VALUES (?,?,?)', (username, password, phone)) conn.commit() return jsonify({'success': True, 'message': '注册成功'}), 200 except sqlite3.IntegrityError: return jsonify({'success': False, 'message': '用户名已存在'}), 400 finally: conn.close() if __name__ == '__main__': app.run(debug=True) ``` ### 登录功能实现 #### 前端部分 前端创建登录页面,包含用户名、密码输入框和登录按钮。用户输入信息后,点击登录按钮,前端将数据发送至后端进行验证。以下是一个简单的 HTML 表单示例: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>苍穹外卖登录</title> </head> <body> <form id="loginForm"> <input type="text" id="username" placeholder="用户名" required> <input type="password" id="password" placeholder="密码" required> <button type="submit">登录</button> </form> <script> document.getElementById('loginForm').addEventListener('submit', function (e) { e.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; // 发送请求到后端 fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: username, password: password }) }) .then(response => response.json()) .then(data => { if (data.success) { alert('登录成功'); // 跳转到首页 window.location.href = '/index'; } else { alert(data.message); } }); }); </script> </body> </html> ``` #### 后端部分 后端接收前端发送的登录信息,从数据库中查询用户信息进行验证。若验证通过,生成并返回令牌(如 JWT)给前端,用于后续的身份验证。以下是一个使用 Python Flask 框架的示例: ```python from flask import Flask, request, jsonify import sqlite3 import jwt from datetime import datetime, timedelta app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' @app.route('/login', methods=['POST']) def login(): data = request.get_json() username = data.get('username') password = data.get('password') # 连接数据库 conn = sqlite3.connect('sky_take_out.db') cursor = conn.cursor() try: # 查询用户信息 cursor.execute('SELECT * FROM user WHERE username =? AND password =?', (username, password)) user = cursor.fetchone() if user: # 生成 JWT 令牌 expiration = datetime.utcnow() + timedelta(hours=1) payload = { 'user_id': user[0], 'username': username, 'exp': expiration } token = jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256') return jsonify({'success': True, 'message': '登录成功', 'token': token}), 200 else: return jsonify({'success': False, 'message': '用户名或密码错误'}), 401 finally: conn.close() if __name__ == '__main__': app.run(debug=True) ``` ### 微信登录实现 苍穹外卖还支持微信登录,在`UserService.java`中定义了微信登录的接口: ```java package com.sky.service; import com.sky.dto.UserLoginDTO; import com.sky.entity.User; public interface UserService { /** * 微信登录 * @param userLoginDTO * @return */ User wxLogin(UserLoginDTO userLoginDTO); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值