阅读视图

发现新文章,点击刷新页面。

GIT历史存在大文件的解决办法

问题触发

在今天迁移项目的时候,触发了一个报错无法推送 refs 到远端。您可以试着运行“拉取”功能,整合您的更改。看了看报错日志发现问题在这里:remote: Find the desired index: b4fc9cd2860327f09ae017bb50ecdadc9fc7d76a, size: 134.975MB, exceeds quota 100MB

可以看出是因为历史大文件导致的无法成功推送。

问题解决

1.找到这个大文件是什么

首先我们使用如下命令去找到这个大文件是什么:

git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '/^blob/ {if ($3 > 100000000) print substr($0,53)}'

一般会返回如下的值

415 XXX.jar

在这个数据里面 415代表的是大小 415M, 后面就是代表着他的文件名称。

2.在历史中删除该文件

现在考虑使用git filter-repo工具

2.1.安装 filter-repo

安装git-filter-repo(如果尚未安装):

pip install git-filter-repo

2.2 根据大小删除文件

这个是根据大小来删除文件的办法 可以删除 100M以上的文件

git filter-repo --strip-blobs-bigger-than 100M

3.清理本地仓库

这一步是为了防止有缓存等影响之后的数据:

git reflog expire --expire=now --all
git gc --prune=now --aggressive
git count-objects -vH  # 检查仓库大小

4.强制推送清理后的历史

git push origin master --force

结束

补充说明

如果上述没有办法解决, 也可以试试 备选方案:手动创建新仓库。

教室选座项目分析

前言

该项目是使用Vue3+Node.js+mongodb数据库实现的,主要实现了用户登录验证、教室选择以及座位选择功能

项目实现效果

选座流程:登录 → 选座 → 选座成功

登录页面,输入学号和姓名,点击登录按钮进行登录,这里的登录的信息是存储在数据库中的,如果输入的学号和姓名信息与数据库中存储的信息不一致,则会有一个弹窗,提示学号必须是23503开头的9位或9-12位数字字符串,只有当输入的学号和姓名信息和数据库中的信息一致时,才能登录成功,并跳转到选座页面,进行选座!

屏幕截图 2025-06-05 095641.png 选座页面,最上面有一个退出按钮,用户点击退出,则会退出登录,返回到登录页面,然后下面是一个选座时间段和教室号的提示,当然每个教室的座位信息也是不同的,它们是实时更新的,在不同的时间段登录,它显示的就是不一样的。用户可以点击不同的教室进行选座,点击座位后,可以点击确认锁定座位,同时会向后端发送选座请求,并对你的token值进行验证,之后会跳转到选择成功的页面,这样选座就成功了。

屏幕截图 2025-06-05 095859.png

在选座成功的页面中,你也可以清楚的看到,具体的教室、时间段、以及日期和星期,你选择的座位在哪里,点击搜索可以进行刷新,如果别人也选座成功了,你也可以看到他的选座情况。点击右侧的学生名单,可以看到在这个时间段上课的学生名单,再次点击就可以恢复原样。

屏幕截图 2025-06-05 100238.png

前端逻辑代码

接下来,我们来看看是如何实现选座的,它的逻辑是怎样的吧!

  1. 退出按钮功能:用户点击退出按钮,就会触发 logout 方法,实现用户退出登录
  2. 信息展示:使用 <pre> 标签展示课程开设时间和可选座时段信息
  3. 时段选择:遍历 ampmArr 数组,渲染选座时段选项
  4. 教室选择:遍历 vmRoomArr 数组渲染教室选项,点击触发 selectRoom 方法加载对应教室数据。
  5. 座位渲染:通过两层 v-for 循环渲染教室座位布局,根据 seat 的值判断是座位还是过道(1表示座位,0表示过道),点击座位触发 handleSeatClick 方法。
<template>
    <div>
        <button @click="logout">退出</button>
        <!-- 添加导航链接 -->
<pre>
    Exprsss开设时间:周一、周五全天 。Python周四 上午
    当天可选座时段:
        上午 08:00-12:00  
        下午 13:00-17:00  
        晚上 19:00-23:00  
        其他时间 记录为 非选座时段
</pre>
        <!-- 上午、下午、晚上 -->
        <section>
            <ul class="ampm">
                <li v-for="(item, index) in ampmArr
                    :class="{ 'pick': ampm === item }"
                    :key="index"
                >
                    {{ item  }}
                </li>
            </ul>
        </section>
        
        <section>
            <ul class="room">
                <li v-for="roomID in vmRoomArr"
                    :class="{ 'pick': vmRoom === roomID }"
                    :key="roomID"
                    @click="selectRoom(roomID)"
                >
                    {{ roomID  }}
                </li>
            </ul>
        </section>
        <!-- 讲台 -->
        <div class="blackboard">讲台</div>
        <div class="classroom">
            <!-- 遍历每一排 -->
            <div v-for="(row, rowIndex) in 教室数据.arr" :key="rowIndex" class="row">

                <!-- 遍历每一个座位或过道 -->
                <div v-for="(seat, colIndex) in row" :key="`${rowIndex}-${colIndex}`"
                    :class="{ 'seat': seat === 1, 'aisle': seat === 0, 'selected': getSeatById(教室数据.layout[rowIndex][colIndex])?.isSelected }"
                    @click="seat === 1 && handleSeatClick(教室数据.layout[rowIndex][colIndex])">
                    <span v-if="seat === 1">{{ 教室数据.layout[rowIndex][colIndex] }}</span>
                </div>
            </div>
        </div>
    </div>
</template>

登录验证

调用 验证token 方法,检查 localStorage 中是否存在 token,若不存在则跳转到登录页

// 判断是否有 token
const hasToken = ref(false);
const 验证token = () => {
    const token = localStorage.getItem('token');
    console.log('token=', token);
    hasToken.value = !!token;
    console.log(hasToken.value);
    if (!hasToken.value) {
        console.log('跳转登录页');
        router.push('/login');
    }
};

教室数据获取

用户选择教室时,通过使用async异步获取数据,调用 fetchClassroomData 方法,发送 GET 请求,获取对应教室的座位布局数据。

// 获取教室数据
const fetchClassroomData = async () => {
    try {
        // 获取本地存储的 token(身份验证凭证
        const token = localStorage.getItem('token');
        let URL = `${BASE_URL}/api/room/want?roomID=${vmRoom.value}`
        // 发送 GET 请求(携带 token 进行身份验证)
        const response = await fetch(URL, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`
            }
        });
        // 检查响应状态
        if (!response.ok) {
            throw new Error(`HTTP 错误!状态:${response.status}`);
        }
        const data = await response.json();
        教室数据.value = data;
    } catch (error) {
        console.error('获取教室数据出错:', error);
    }
};

onMounted(() => {
    console.log('组件已挂载,检查 token...');
    验证token();
    // 组件挂载后获取教室数据
    fetchClassroomData();
});

选座功能

用户点击座位就会触发 handleSeatClick 方法,先更新本地座位选中状态,弹出确认框,确认后发送 POST 请求提交选座信息

选中状态管理

  • 先将所有座位的 isSelected 设为 false,再将当前点击的座位设为 true
  • 这确保同一时间只有一个座位被选中

座位锁定流程

  • 弹出确认对话框,用户确认后发送 POST 请求,锁定座位。
  • 根据服务器返回的状态码(code)处理结果:
  • 成功(code === 10000):显示提示并跳转到查看页面。
  • 失败:显示错误信息,锁定座位出错
// 根据 seat_id 获取座位信息
const getSeatById = (seatId) => {
    return 教室数据.value.data.find(seat => seat.seat_id === seatId);
};

const handleSeatClick = async (seatId) => {
    // 先将所有座位的选中状态置为 false
    教室数据.value.data.forEach(seat => {
        seat.isSelected = false;
    });

    // 将当前点击的座位选中状态置为 true
    const currentSeat = getSeatById(seatId);
    if (currentSeat) {
        currentSeat.isSelected = true;
    }

    console.log(`你点击了座位 ID 为 ${seatId} 的座位`);
    if (confirm(`确认锁定座位 ID 为 ${seatId} 的座位吗?`)) {
        let 请求参数 = {
            seat_id: seatId,
            roomID: 教室数据.value.roomID,
            token: localStorage.getItem('token'),
        }
        console.log(请求参数)
        try {
            const 响应 = await fetch(`${BASE_URL}/api/seat/pick`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(请求参数)
            });
            if (!响应.ok) {
                throw new Error(`HTTP 错误!状态:${响应.status}`);
            }
            
            // 处理响应数据
            const { code, text } = await 响应.json();   //数据

            if (code === 10000) {
                alert(text);
                let 参数 = {
                    name:'look',
                    query:{
                        roomID:vmRoom.value,
                        ampm:ampm.value,
                    }
                }
                
                router.push( 参数 );
            } else {
                alert(text);
            }
            
            // 更新本地座位状态
            if (currentSeat) {
                currentSeat.isLocked = true;
            }
        } catch (error) {
            console.error('锁定座位出错:', error);
        }
    } else {
        // 如果取消锁定,将当前座位的选中状态置为 false
        if (currentSeat) {
            currentSeat.isSelected = false;
        }
    }
};

连接MongoDB数据库

需要用到的数据都存储在MongoDB数据库中,为了方便调用 屏幕截图 2025-06-05 154103.png

// db.js
const { MongoClient } = require('mongodb');
// 数据库连接配置
class DB {
    constructor(数据库名="云上教室") {
        this.uri = "mongodb://localhost:27017/云上教室";
        this.uri = "mongodb://’这里放你的IPV4地址‘:27017/云上教室";
        //this.uri = "mongodb://127.0.0.1:27017/云上教室";    //上线设置为本机IP 速度更快

        this.client = new MongoClient(this.uri);
        this.db = this.client.db(数据库名);
        return this.db;
    }

    // 封装连接数据库的函数
    async connect() {
        try {
            await this.client.connect();
            console.log('已连接到 MongoDB');
            return this.db;
        } catch (error) {
            console.error('连接到 MongoDB 时出错:', error);
            throw error;
        }
    }

    // 封装关闭数据库连接的函数
    async close() {
        try {
            await this.client.close();
            console.log('已断开与 MongoDB 的连接');
        } catch (error) {
            console.error('断开与 MongoDB 的连接时出错:', error);
            throw error;
        }
    }
}
module.exports = new DB();

后端逻辑代码

类文件

用于获取当前的时间信息(上午/下午、星期和日期)

class 工具 {
    constructor() {
    }
    获取上午下午() {
        const now = new Date();
        const hours = now.getHours();
        if (hours >= 8 && hours < 12) {
            return '上午';
        } else if (hours >= 13 && hours < 17) {
            return '下午';
        } else if (hours >= 19 && hours < 23) {
            return '晚上';
        } else {
            return '非选座时段';
        }
    }
    获取星期() {
        const daysOfWeek = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        const now = new Date();
        const dayIndex = now.getDay();
        return daysOfWeek[dayIndex];
    }

    //获取当前日期,格式为 YYYY-MM-DD
    获取当前日期() {
        const now = new Date();
        const year = now.getFullYear();
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const day = String(now.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    }
}// 定义 工具 类

// 导出 工具 类
module.exports = 工具;

路由处理函数

/api/room/list,用于连接数据库,获取所有教室的数据

屏幕截图 2025-06-05 162303.png/api/room/want,用于获取指定教室在特定日期和上下午时段的座位信息

路由.get('/api/room/want', async (req, res ) => {
    //一些参数 动态生成 Data
    let { roomID = 506 } = req.query;
    //发起请求时必须在有效时段
    //服务器设置时间
    let ampm = tool.获取上午下午();
    let week = tool.获取星期();
    let date = tool.获取当前日期();
    // 数据校验  注意严格 区分数据类型 、大小写
    // RoomID  roomID ='501'
    // 转换数字类型 在 Express 里,通过 req.query 获取的查询字符串参数默认都是字符串类型,这是因为 HTTP 请求中的查询字符串是以文本形式传输的,Express 不会自动对这些参数进行类型转换
    roomID = parseInt(roomID);
    //查看教室座位 数量
    const 教室 =await  DB.collection("教室");
    let room = await 教室.findOne(
        { roomID },                // 查询条件
        { projection: { _id: 0 } } // 投影参数
    );

    // 关联数据 根据的应该是 date 决定,不是由Week决定的
    const 座位记录 =await DB.collection("座位记录");
    let 参数 = {
        roomID,
        date,
        ampm
    }
    let 投影参数 = {
        projection: { _id: 0,roomID:0,date:0,ampm:0 }
    }
    const seatArr = await 座位记录.find(参数,投影参数).sort({ time: 1 }).toArray();
    console.table(seatArr)

    // 确保 room 不为 null 再进行操作
    const 合并数据 =room ?  room.data?.map(座位 => {
        const 记录 = seatArr.find(记录 => 记录.seat_id === 座位.seat_id);
        return {
            ...座位,
            ...记录, // 保留原始记录数据
            is_free: 记录 ? false : 座位.is_free // 动态设置状态
        };
    }) : [];
    console.log( `--------------------------------------------------` )
    console.log( 合并数据 )
    res.json({
        ...(room || {}),
        // 过滤完全无记录的座位,先检查元素是否为对象,再检查属性数量
        data: 合并数据?.filter(item => typeof item === 'object' && item!== null && Object.keys(item).length > 3)
    });

/api/room/look,用于查看指定教室在特定日期和上下午时段的座位详情

//查看教室座位 详情
路由.get('/api/room/look', async (req, res ) => {
    let { roomID: rawRoomID, ampm: rawAmpm,date:dateFE  } = req.query;

    // 参数有效性校验
    roomID = parseInt(rawRoomID);
    if (isNaN(roomID)) roomID = 506; // 无效值时使用默认值
    ampm = ['上午', '下午'].includes(rawAmpm) ? rawAmpm : tool.获取上午下午();
    date =dateFE ?? tool.获取当前日期();
    // 数据校验  注意严格 区分数据类型 、大小写
    // RoomID  roomID ='501'
    // 转换数字类型 在 Express 里,通过 req.query 获取的查询字符串参数默认都是字符串类型,这是因为 HTTP 请求中的查询字符串是以文本形式传输的,Express 不会自动对这些参数进行类型转换
    roomID = parseInt(roomID);
    const 集合 =await  DB.collection("教室");
    let room = await 集合.findOne(
        { roomID },                // 查询条件
        { projection: { _id: 0 } } // 投影参数
    );
    // 新增字段过滤
    // const { _id, ...教室数据 } = room ? room : {};  // 处理空值情况
    console.log('room',room);  // 现在输出已移除 _id 的数据
    // 关联数据 根据的应该是 date 决定,不是由Week决定的
    const 集合2 =await DB.collection("座位记录");
    let 参数 = {
        roomID,
        date,
        ampm
    }
    let 投影参数 = {
        projection: { _id: 0,roomID:0,date:0,ampm:0 }
    }
    const seatArr = await 集合2.find(参数,投影参数).sort({ time: 1 }).toArray();
    console.table(seatArr)
    // 确保 room 不为 null 再进行操作
    const 合并数据 =room ?  room.data?.map(座位 => {
        const 记录 = seatArr.find(记录 => 记录.seat_id === 座位.seat_id);
        return {
            ...座位,
            ...记录, // 保留原始记录数据
            is_free: 记录 ? false : 座位.is_free // 动态设置状态
        };
    }) : [];
    console.log( `--------------------------------------------------` )
    console.log( 合并数据 )
    res.json({
        ...(room || {}),
        // 过滤完全无记录的座位,先检查元素是否为对象,再检查属性数量
        data: 合并数据?.filter(item => typeof item === 'object' && item!== null && Object.keys(item).length > 3)
    });
});

获取座位列表和选择座位

用于处理时间信息和座位状态的更新

  1. 定义了一个输入验证函数 validateInput,用于验证 tokenseat_id 是否有效
  2. 使用 validateSeat 函数验证座位号是否在有效范围内(1-154)。
  3. 使用 validateToken 函数验证 token 的格式是否正确。
// 封装输入验证逻辑
const validateInput = (token, seat_id, ip) => {
    if (!token || !seat_id) {
        const msg = { text: `🐮,🐯,🐰,🐱,🐲,🐳,🐴,🐵 。 token和seat_id都是必需的!👀 🐶,🐷,🐸,🐹,🐺,🐻,🐼,🐽,🐾,🐿️, ` };
        report(ip, msg);
        return msg;
    }

    if (!validateSeat(seat_id)) {
        const msg = { text: `🐱座位号应该在 154(含)以内!请核对真实座位,使用数字 ` };
        report(ip, msg);
        return msg;
    }

    if (!validateToken(token)) {
        const msg = { text: `🐲🐲🐲🐲门票(token)应通过node 1获得,格式为 XXXX-XXXX-XXXX` };
        report(ip, msg);
        return msg;
    }
    return null;
};

总结

以上就是云上课堂选座的内容,这个项目提供了一个线上可视化选座的平台,操作步骤简单,不仅为学生提供了自由选座,也让老师能够清楚地看出教室的选座情况。

自动化测试、前后端mock数据量产利器:Chance.js深度教程

1. 什么是Chance.js?

  • 超轻量级(压缩后仅十几KB)
  • API极其丰富,支持姓名、年龄、地址、电话、邮箱、数字、布尔、图片、时间、uuid等几十种数据类型
  • 无外部依赖,适用于Node、前端浏览器等环境
  • 自定义性强,可控制数据范围、格式、多语言等

2. 快速安装与引入

Node环境:

npm install chance
const Chance = require('chance');
const chance = new Chance();

前端浏览器(CDN):

<script src="https://cdn.jsdelivr.net/npm/chance@1.1.11/dist/chance.min.js"></script>
<script>
  var chance = new Chance();
</script>

3. 生成常用假数据

基础属性

console.log(chance.name());       // 随机姓名 e.g. 'Bettie Thornton'
console.log(chance.age());        // 随机年龄 e.g. 54
console.log(chance.gender());     // 随机性别 'Male' or 'Female'
console.log(chance.city());       // 随机城市 e.g. 'Cagaeh'
console.log(chance.country());    // 随机国家 e.g. 'DE'
console.log(chance.phone());      // 随机电话号 e.g. (766) 998-5343
console.log(chance.email());      // 随机邮箱 e.g. 'eptof@wahehasa.gm'
console.log(chance.url());        // 随机网址 e.g. http://gu.bv/sur

数值、布尔、数组

chance.integer({ min: 100, max: 999 });  // 生成指定范围整数
chance.floating({ min: 0, max: 1, fixed: 3 }); // 小数三位
chance.bool();         // true或false
chance.pickone(['red', 'blue', 'green']); // 随机挑选
chance.shuffle([1,2,3,4,5]); // 随机打乱数组

唯一标识与特殊格式

chance.guid();      // GUID/UUID
chance.hash();      // 随机哈希
chance.ip();        // 随机IPv4
chance.natural();   // 随机自然数
chance.timestamp(); // 随机时间戳
chance.date({ year: 2024 }); // 指定年份的日期对象

4. 控制数据范围与定制

Chance 几乎所有API都可以定制参数——范围、位数、样式:

chance.name({ middle: true }); // 带中间名
chance.age({ type: 'child' });      // 0-12岁
chance.phone({ country: 'fr' }); // 法国电话
chance.string({ length: 8, pool: '0123456789ABCDEF' }); // 8位十六进制字符串

5. 批量生成mock数据

常见于表格、列表、接口数据填充:

const list = Array.from({ length: 10 }).map(() => ({
  id: chance.guid(),
  name: chance.name(),
  age: chance.age(),
  email: chance.email(),
  city: chance.city()
}));
console.table(list);

6. 测试、数据可视化、前端mock应用场景

  • 前端开发:接口返回数据无须后端,直接mock填充表单/列表
  • 测试用例:各种格式边界、随机性输入
  • 数据可视化:大规模模拟用户、订单、网络数据
  • 安全测试:大量边界、极端字符串、号码覆盖


7. 常见坑和小贴士

  • 随机字符串如需高复杂度可指定字符池
  • 路径、邮箱、手机号可定制格式,避免后续格式错误
  • 生成大量 mock数据时,推荐批量函数或map

结语

Chance.js 用最简单的方式解决“前端造假数据难题”,是你开发、测试、数据展现的最佳拍档。赶快试试看,用它让你的 mock 代码不再拖沓!

本文只是简单的罗列了Chance.js的一些用法,更多用法可以查看官网


如果你喜欢本教程,记得点赞+收藏!关注我获取最新JavaScript开发干货。

❌