吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3039|回复: 25
收起左侧

[其他原创] rust值日排班工具

  [复制链接]
a121bc 发表于 2023-6-11 19:17

rust值日排班工具


公司里每个月都需要做一份卫生值日安排表,一直都是人工编辑安排,需要根据人数、区域和工作日进行汇总分析,人工编辑难免出错,因为学过一段rust于是想用rust做一个工具自动生成

一直想不通如何分配人员,还有如何分配最公平,加上rust所有权和借用引用概念不熟悉绕了点小弯子,多花了点时间,现奉上源码,希望大家指正


use std::{env, io};
use std::collections::{HashMap, HashSet};
use std::io::BufRead;

use chrono::{Datelike, Local};
use rand::{Rng, thread_rng};
use rand::prelude::SliceRandom;
use rust_xlsxwriter::{Format, FormatAlign, FormatBorder, Workbook, XlsxError};

fn main() -> Result<(), XlsxError> {
    let stdin = io::stdin();
    // 参与人员
    let mut staff:Vec<&str> = vec![
        "赵云", "马超", "黄忠", "诸葛亮",
        "刘备", "关羽", "张飞", "曹操",
        "张辽", "孙权", "周瑜", "黄盖",
        "华佗", "貂蝉", "大乔", "小乔"
    ];
    println!("**************************************");
    println!("**************************************");
    println!("****** 欢迎使用员工值日生成系统 ******");
    println!("**************************************");
    println!("**************************************");
    println!();
    println!("描述:随机分配员工顺序,并按照每人值日总次数分配,同时兼顾左右侧分配平衡");
    println!();
    println!("tips:按回车跳过可取默认值");
    println!("tips:输出文件到当前目录下");
    println!();
    println!();
    println!("1/3 请输入参与人员(不能少于6个且不能重复),使用空格键分隔,默认:赵云 马超 黄忠 诸葛亮 刘备 关羽 张飞");
    let mut staff_line = String::new();
    loop {
        stdin.lock().read_line(&mut staff_line).unwrap();
        staff_line = staff_line.trim().to_string();
        // 输入项超过6个
        if (staff_line.split_whitespace().collect::<HashSet<&str>>().len() == staff_line.split_whitespace().count()
            && staff_line.split_whitespace().count() >= 6 )
            || staff_line.is_empty() {
            break;
        } else {
            staff_line.clear();
            println!("参与人员少于6个或有重复,请重新输入");
        }
    }

    if !staff_line.is_empty() {
        staff = staff_line.trim()
            .split_whitespace().collect();
    }

    // 年月
    let now = Local::now();
    let mut year = now.year();
    let mut month = now.month();
    println!("2/3 请输入年份,默认当年:{}", year);
    let mut year_line = String::new();
    loop {
        stdin.lock().read_line(&mut year_line).unwrap();
        if !year_line.trim().is_empty() {
            if let Ok(y) = year_line.trim().parse::<i32>() {
                year = y;
            } else {
                year_line = String::new();
                println!("年份解析出错,请重新输入或回车跳过");
                continue;
            }
        }
        break;
    }

    println!("3/3 请输入月份,默认当月: {}", month);
    let mut month_line = String::new();
    loop {
        stdin.lock().read_line(&mut month_line).unwrap();
        if !month_line.trim().is_empty() {
            if let Ok(m) = month_line.trim().parse::<u32>(){
                month = m;
            } else {
                month_line = String::new();
                println!("月份解析出错,请重新输入或回车跳过");
                continue;
            }
        }
        break;
    }

    let mut total_group = vec![];
    let mut staff_count: HashMap<&str, usize> = HashMap::new();
    let mut left_count: HashMap<&str, usize> = HashMap::new();
    for s in &staff {
        staff_count.insert(*s, 0);
        left_count.insert(*s, 0);
    }

    staff.shuffle(&mut thread_rng());
    for _ in 1..30 {
        let arr: Vec<_> = staff.chunks_exact(3).collect();
        let arr: Vec<_> = arr.iter().map(|&e| e).take(arr.len() - arr.len()%2).collect();
        for x in 0..arr.len(){
            total_group.push(arr[x].iter().map(|s| s.to_string()).collect::<Vec<String>>());
            for &i in arr[x] {
                let c = staff_count.get(i).unwrap_or(&0);
                staff_count.insert(i, c + 1);
                // 在左侧出现的次数
                if x%2 == 0 {
                    let c = left_count.get(i).unwrap_or(&0);
                    left_count.insert(i, c + 1);
                }
            }
        }
        // 按照出现次数排序
        staff.sort_by(|&a,&b| {
            // 总次数排序
            let mut ac = staff_count.get(&a).unwrap();
            let mut bc = staff_count.get(&b).unwrap();
            // 总次数相同 则按左侧出现次数排序
            if ac == bc {
                ac = left_count.get(&a).unwrap();
                bc = left_count.get(&b).unwrap();
            }

            // 两侧不同则排序,否则随机排序
            if ac != bc {
                ac.cmp(bc)
            } else {
                if thread_rng().gen_bool(0.5) {
                    a.cmp(&b)
                } else {
                    b.cmp(&a)
                }
            }
        });

        if total_group.len()>=60 {
            break;
        }
    }

    let days_in_month = num_days_month(year, month);
    // 居中格式
    let title_format = Format::new()
        .set_border(FormatBorder::Thin)
        .set_font_size(20)
        .set_font_name("微软雅黑")
        .set_align(FormatAlign::Center)
        .set_align(FormatAlign::VerticalCenter)
        ;
    // 标题字段
    let field_formst = Format::new()
        .set_font_name("微软雅黑")
        .set_border(FormatBorder::Thin)
        .set_font_size(12)
        .set_bold()
        .set_align(FormatAlign::Center)
        .set_align(FormatAlign::VerticalCenter)
        ;

    // 居中格式
    let border_format = Format::new()
        .set_font_name("微软雅黑")
        .set_border(FormatBorder::Thin)
        .set_font_size(12)
        .set_align(FormatAlign::Center)
        .set_align(FormatAlign::VerticalCenter)
        ;
    let end_format= Format::new()
        .set_font_name("微软雅黑")
        .set_border(FormatBorder::Thin)
        .set_font_size(12)
        .set_align(FormatAlign::VerticalCenter)
        .set_text_wrap()
        ;

    let mut wb = Workbook::new();
    let ws = wb.add_worksheet();
    // 标题
    let title = format!("{}年{}月值日安排", year, month);
    ws.merge_range(0, 0, 0, 4, title.as_str(), &title_format)?;
    ws.set_row_height(0, 38)?;
    ws.set_column_width(0, 11)?;
    ws.set_column_width(1, 10)?;
    ws.set_column_width(2, 28)?;
    ws.set_column_width(3, 28)?;
    // 添加字段
    ws.write_with_format(1, 0, "日期", &field_formst)?;
    ws.write_with_format(1, 1, "星期", &field_formst)?;
    ws.write_with_format(1, 2, "左大厅(包含卫生间)", &field_formst)?;
    ws.write_with_format(1, 3, "右大厅(包含吸烟区)", &field_formst)?;
    ws.write_with_format(1, 4, "绿植", &field_formst)?;
    // 实际计数
    let mut fac_i = 0;
    // 周次
    let mut week_i = 0;
    // 行数
    let mut data_row = 2;
    // 月内星期开始
    let mut w_start = 2;
    // 月内星期结束
    let mut w_end;
    for day in 1..=days_in_month {
        let date = Local::now()
            .with_year(year).unwrap()
            .with_month(month).unwrap()
            .with_day(day).unwrap();
        // 设置行高
        ws.set_row_height(data_row, 20)?;

        // 日期
        let day_str = format!("{}月{}日", month, day);
        // 星期
        let weekday = to_week_name(date.weekday());
        // 左大厅
        let left_staff = total_group[fac_i*2].join("、");
        // 右大厅
        let right_staff = total_group[fac_i*2+1].join("、");

        // 日期
        ws.write_with_format(data_row, 0, day_str, &field_formst)?;
        // 星期
        ws.write_with_format(data_row, 1, weekday, &field_formst)?;

        if vec![chrono::Weekday::Sat, chrono::Weekday::Sun].contains(&date.weekday()) {
            ws.merge_range(data_row, 2, data_row, 4, "",&border_format)?;
        } else {
            // 左大厅
            ws.write_with_format(data_row, 2, left_staff, &border_format)?;
            // 右大厅
            ws.write_with_format(data_row, 3, right_staff, &border_format)?;
            fac_i += 1;
        }

        // 月初或是周一 设置开始行
        if day == 1 || date.weekday() == chrono::Weekday::Mon {
            w_start = data_row;
        }

        // 月末或周五 设置结束行
        if (day == days_in_month && !vec![chrono::Weekday::Sat, chrono::Weekday::Sun].contains(&date.weekday()) ) || date.weekday() == chrono::Weekday::Fri {
            w_end = data_row;
            if w_start == w_end {
                // 最后一天单条
                ws.write_with_format(w_start, 4,staff[week_i], &border_format)?;
            } else {
                // 绿植
                ws.merge_range(w_start, 4, w_end, 4, staff[week_i], &border_format)?;
            }

            week_i += 1;
        }

        data_row += 1;
    }
    let counting = staff_count.iter()
        .map(|(k, v)| format!("{}({}次)", *k, v))
        .collect::<Vec<String>>()
        .join(" ");
    let ending = format!("值日汇总:{}", counting);

    ws.merge_range(data_row,0, data_row, 4, ending.as_str(), &end_format)?;
    ws.set_row_height(data_row, 60)?;
    let mut cur_dir = env::current_dir().unwrap();
    cur_dir.push(format!("{title}.xlsx"));
    wb.save(cur_dir.to_str().unwrap())?;
    Ok(())

}

/// 星期转汉字
fn to_week_name(weekday: chrono::Weekday) -> &'static str {
    let weekday = match weekday {
        chrono::Weekday::Mon => "星期一",
        chrono::Weekday::Tue => "星期二",
        chrono::Weekday::Wed => "星期三",
        chrono::Weekday::Thu => "星期四",
        chrono::Weekday::Fri => "星期五",
        chrono::Weekday::Sat => "星期六",
        chrono::Weekday::Sun => "星期日",
    };
    weekday
}

/// 获取月内的最大天数
fn num_days_month(year: i32, month: u32) -> u32 {
    let days: u32 = match month {
        2 if year % 4 == 0 && year % 100 != 0 || year % 400 == 0 => 29,
        2 => 28,
        4 | 6 | 9 | 11 => 30,
        _ => 31,
    };
    days
}


效果图

控制台界面

控制台界面

生成的文档

生成的文档

生成的文档

免费评分

参与人数 9吾爱币 +15 热心值 +8 收起 理由
lanhao10 + 1 我很赞同!
三滑稽甲苯 + 2 + 1 用心讨论,共获提升!
wbzb + 1 + 1 热心回复!
wushaominkk + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
啊歪1号 + 1 + 1 我很赞同!
38342175 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
追逐飞翔 + 1 + 1 用心讨论,共获提升!
xlycaq1 + 1 + 1 谢谢分享!大佬麻烦上个成品链接
lijun888 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| a121bc 发表于 2023-6-12 08:49
xlycaq1 发表于 2023-6-12 08:25
谢谢分享!大佬麻烦上个成品链接

https://wwwc.lanzoub.com/i0nvC0yxio7c
密码:52pj
fishyoyo 发表于 2023-6-12 11:22
我认为还是有点小瑕疵   6.15日星期四  曹操 大乔 小乔?   你怕不是被曹操封了官
xlycaq1 发表于 2023-6-12 08:25
dkzzlf 发表于 2023-6-12 08:36
下载试试看
38342175 发表于 2023-6-12 08:59
谢谢分享
kingc138 发表于 2023-6-12 09:20
厉害!点赞
醉酒听风 发表于 2023-6-12 09:24
挺不错的工具,源码改改用处挺大的,感谢分享
schoolclub 发表于 2023-6-12 09:34
a121bc 发表于 2023-6-12 08:49
https://wwwc.lanzoub.com/i0nvC0yxio7c
密码:52pj

感谢大佬分享,拿来研究下~~~
rxxcy 发表于 2023-6-12 09:40
编译器劝退...
娟然俊逸 发表于 2023-6-12 09:56
马超、刘备、关羽、赵云,等等 蜀国有话要说了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表