当前位置: 首页 > news >正文

使用Go语言生成Excel任务表依赖图(Markdown文件mermaid图)

一、前言

在游戏中,任务是非常常见的玩法,可能会有主线任务,支线任务以及其它一些类型的任务,各任务可能还会有前置任务,即需要完成某个任务之后,才能做当前任务。在游戏开发中,配置表可以使用Excel来编辑,如果是任务表,可能会是如下配置方式:

TaskIDTaskTitlePreTask
10任务100
20任务200
11任务1110
21任务2120

当任务比较多的时候,它们的依赖关系将变得不直观,很容易出错,出错也不容易发现。

有没比较直观的方式进行查看,排错呢?笔者想到了目前非常流程的Markdown文件,它可以简单地通过文本的方式输入然后输出强大的各种图表。这里就可以使用mermaid图来直观展现。

关于mermaid图可以去官网https://mermaid.js.org/intro/查看用例。

下图为生成后的效果图:

0:无效任务ID
21:任务21
20:任务20
11:任务11
10:任务10

注意:mermaid图在渲染时,如果不设置subgraph则可能会出现乱序问题,即不是按代码中出现的顺序渲染。

二、实现

为了方便Go读取Excel,需要使用相关的Excel库,笔者使用excelize库。

根据前面的效果图,可以知道,这其实就是一个深度优先的树,实现方式有两种,一种是使用递归的方式来实现,这种方式实现起来简单,但是如果层次很深,那可能会出现栈溢出;另一种方式就是使用栈的方式来实现,将每一层节点先压栈,然后从栈顶取出一个节点然后再将其所有子节点入栈,再从栈顶取出一个节点处理,依此类推,直到栈中所有节点处理完毕。

下面列出使用递归方式实现的版本:

/*
MIT License# Copyright (c) 2023 WittonBellPermission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package mainimport ("flag""fmt""os""path/filepath""strings""github.com/xuri/excelize/v2"
)var taskIDField string
var taskTitleField string
var preTaskField string
var noCaseSensitive bool // 是否不区分大小写
var fieldNameRow uint    // 字段名所在行号
var dataStartRow uint    // 数据开始行号type node struct {taskID    stringtaskTitle string
}type multiMap map[string][]*nodefunc (slf multiMap) Add(key string, nd *node) {if len(slf) == 0 {slf[key] = []*node{nd}} else {slf[key] = append(slf[key], nd)}
}func (slf multiMap) Get(key string) []*node {if slf == nil {return nil}return slf[key]
}func (slf multiMap) Del(key string) {delete(slf, key)
}func searchKeyCol(rows *excelize.Rows) (TaskIDCol, PreTaskIDCol, TitleCol int) {row, err := rows.Columns()if err != nil {fmt.Println(err.Error())}for i, col := range row {name := colif noCaseSensitive {name = strings.ToLower(col)}if name == preTaskField {PreTaskIDCol = i + 1} else if name == taskIDField {TaskIDCol = i + 1} else if name == taskTitleField {TitleCol = i + 1}}return
}func readExcel(filePath string) multiMap {fd, err := excelize.OpenFile(filePath)if err != nil {fmt.Printf("读取文件`%s`失败", filePath)return nil}defer func() {fd.Close()}()TaskIDCol, PreTaskIDCol, TitleCol := -1, -1, -1sheetName := fd.GetSheetName(0)rows, err := fd.Rows(sheetName)if err != nil {return nil}defer func() {rows.Close()}()m := multiMap{}for i := 1; rows.Next(); i++ {if i == int(fieldNameRow) {TaskIDCol, PreTaskIDCol, TitleCol = searchKeyCol(rows)isOk := trueif TaskIDCol < 0 {isOk = falsefmt.Printf("要求字段名:%s\n", taskIDField)}if PreTaskIDCol < 0 {isOk = falsefmt.Printf("要求字段名:%s\n", preTaskField)}if TitleCol < 0 {isOk = falsefmt.Printf("要求字段名:%s\n", taskTitleField)}if !isOk {return nil}}if i < int(dataStartRow) {continue}TaskIDCell, err := excelize.CoordinatesToCellName(TaskIDCol, i)if err != nil {continue}PreTaskIDCell, err := excelize.CoordinatesToCellName(PreTaskIDCol, i)if err != nil {continue}TitleColCell, err := excelize.CoordinatesToCellName(TitleCol, i)if err != nil {continue}TaskID, err := fd.GetCellValue(sheetName, TaskIDCell)if err != nil || TaskID == "" {continue}Title, err := fd.GetCellValue(sheetName, TitleColCell)if err != nil || Title == "" {continue}PreTaskID, err := fd.GetCellValue(sheetName, PreTaskIDCell)if err != nil {continue}if PreTaskID == "" {PreTaskID = "0"}m.Add(PreTaskID, &node{taskID: TaskID, taskTitle: Title})}return m
}func usage() {w := flag.CommandLine.Output()fmt.Fprintf(w, "%s 应用程序是将Excel任务表中的关系转换成Markdown的mermaid图,方便使用Markdown工具直观地查看任务依赖。", filepath.Base(os.Args[0]))fmt.Fprintln(w)fmt.Fprintf(w, "命令格式:%s -hr [字段所在行号] -dr [数据起始行号] [-nc] -id [任务ID字段名] -t [任务标题字段名] -pid [前置任务ID字段名] -o <输出文件> <Excel文件路径>", filepath.Base(os.Args[0]))fmt.Fprintln(w)flag.CommandLine.PrintDefaults()fmt.Fprintln(w, "  -h")fmt.Fprintln(w, "    \t显示此帮助")
}func main() {var outputFile stringflag.CommandLine.Usage = usageflag.BoolVar(&noCaseSensitive, "nc", false, "字段名不区分大小写")flag.UintVar(&fieldNameRow, "hr", 1, "字段所在行号")flag.UintVar(&dataStartRow, "dr", 2, "数据起始行号")flag.StringVar(&taskIDField, "id", "ID", "-id [任务ID字段名]")flag.StringVar(&taskTitleField, "t", "Title", "-t [任务标题字段名]")flag.StringVar(&preTaskField, "pid", "PreTask", "-pid [前置任务ID字段名]")flag.StringVar(&outputFile, "o", "任务图.md", "-o <输出文件>")flag.Parse()if flag.NArg() < 1 {usage()return}if noCaseSensitive {taskIDField = strings.ToLower(taskIDField)taskTitleField = strings.ToLower(taskTitleField)preTaskField = strings.ToLower(preTaskField)}mapTask := readExcel(flag.Arg(0))buildGraph(mapTask, outputFile)
}func buildGraph(mapTask multiMap, outputFile string) {graph := "```mermaid\ngraph TB\n"graph += "subgraph  \n"root := mapTask.Get("0")for _, v := range root {graph += visit(rootNodeName, v, mapTask)}graph += "end\n"graph += "```"os.WriteFile(outputFile, []byte(graph), os.ModePerm)fmt.Println("完成")
}func visit(parent string, nd *node, mapTask multiMap) string {slice := mapTask.Get(nd.taskID)graph := fmt.Sprintf("%s --> %s:%s\n", parent, nd.taskID, nd.taskTitle)if parent == rootNodeName {graph += "subgraph  \n"}for _, x := range slice {graph += visit(fmt.Sprintf("%s:%s", nd.taskID, nd.taskTitle), x, mapTask)}mapTask.Del(nd.taskID)if parent == rootNodeName {graph += "end\n"}return graph
}

使用栈实现的版本笔者放在excelTask2md了。

相关文章:

使用Go语言生成Excel任务表依赖图(Markdown文件mermaid图)

一、前言 在游戏中&#xff0c;任务是非常常见的玩法&#xff0c;可能会有主线任务&#xff0c;支线任务以及其它一些类型的任务&#xff0c;各任务可能还会有前置任务&#xff0c;即需要完成某个任务之后&#xff0c;才能做当前任务。在游戏开发中&#xff0c;配置表可以使用…...

C语言和C++的区别在哪?如何自学C++?

C语言和C是两种不同的编程语言&#xff0c;它们在语法、特性和用途上有一些区别。以下是C语言和C的一些主要区别&#xff1a; 面向对象编程&#xff1a;C是一种支持面向对象编程的语言&#xff0c;它在C语言的基础上添加了类、对象、继承、多态等面向对象的特性。而C语言是一种…...

功能强大的开源数据中台系统 DataCap 1.13.0 发布

推荐一套基于 SpringBoot 开发的简单、易用的开源权限管理平台&#xff0c;建议下载使用: https://github.com/devlive-community/authx 推荐一套为 Java 开发人员提供方便易用的 SDK 来与 OpenAI 的 API 进行交互组件&#xff1a;https://github.com/devlive-community/openai…...

JTS Self-intersection异常TopologyException: side location conflict解决办法

JTS Self-intersection异常TopologyException: side location conflict解决办法 举例&#xff1a;问题围栏 MULTIPOLYGON (((114.0905685 32.1120567, 114.0905685 32.112957, 114.0905685 32.1138535, 114.0905685 32.1147537, 114.0905685 32.115654, 114.0905685 32.11655…...

Maven: No compiler is provided in this environment.

在Eclipse中运行Maven项目&#xff0c;报错&#xff1a; No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? 解决方法&#xff1a; Windows > Preferences > Java > Installed JREs > Add > Standard VM,…...

.NET-10. 其他-VSTO+VBA

VSTOVBA 前言VSTO 外接程序介绍&#xff1a;VSTO参考链接&#xff1a;VSTO 例子&#xff1a; VBA:参考链接&#xff1a; 前言 主要用于Excel插件。 VSTO 外接程序介绍&#xff1a; Excel、Word、PowerPoint、Project、Visio等等Office应用程序 相对简单 VSTO参考链接&#x…...

相机传感器格式与镜头光圈参数

相机靶面大小 CCD/CMOS图像传感器尺寸&#xff08;sensor format&#xff09;1/2’‘、1/3’‘、1/4’实际是多大 1英寸——靶面尺寸为宽12.7mm*高9.6mm&#xff0c;对角线16mm。 2/3英寸——靶面尺寸为宽8.8mm*高6.6mm&#xff0c;对角线11mm。 1/2英寸——靶面尺寸为宽6.…...

Android 设置头像(拍照获取、相册获取、裁剪照片)

在Android原生态开发过程中&#xff0c;往往会设计到用户头像的设置问题&#xff0c;一般来讲设置头像需要用到拍照、获取照片、存储照片、裁剪照片、显示照片等问题&#xff0c;本文将一步一步的进行说明讲解。 首先需要强调几点我在开发过程中遇到的问题。 权限问题&#xf…...

android开发之Android 自定义滑动解锁View

自定义滑动解锁View 需求如下&#xff1a; 近期需要做一个类似屏幕滑动解锁的功能&#xff0c;右划开始&#xff0c;左划暂停。 需求效果图如下 实现效果展示 自定义view如下 /** Desc 自定义滑动解锁View Author ZY Mail sunnyfor98gmail.com Date 2021/5/17 11:52 *…...

CAD绘制法兰、添加光源、材质并渲染

首先绘制两个圆柱体&#xff0c;相互嵌套 在顶部继续绘制圆柱体&#xff0c;这是之后要挖掉的部分 在中央位置绘制正方形 用圆角工具&#xff1a; 将矩形的四个角分别处理&#xff0c;效果&#xff1a; 用拉伸工具 向上拉伸到和之前绘制的圆柱体高度齐平 绘制一个圆柱体&#…...

ChatGPT访问流量下降的原因分析

​自从OpenAI的ChatGPT于11月问世以来&#xff0c;这款聪明的人工智能聊天机器人就席卷了全世界&#xff0c;人们在试用该工具的同时也好奇该技术到底将如何改变我们的工作和生活。 但近期Similarweb表示&#xff0c;自去ChatGPT上线以来&#xff0c;该网站的访问量首次出现下…...

干货 | 详述 Elasticsearch 向量检索发展史

1. 引言 向量检索已经成为现代搜索和推荐系统的核心组件。 通过将复杂的对象&#xff08;例如文本、图像或声音&#xff09;转换为数值向量&#xff0c;并在多维空间中进行相似性搜索&#xff0c;它能够实现高效的查询匹配和推荐。 图片来自&#xff1a;向量数据库技术鉴赏【上…...

mysql常见面试题,高频题目放送

互联网的产品架构是包含这接入层&#xff0c;逻辑处理以及储存层的&#xff0c;其中储存层承载着较多的数据以及持久化的任务&#xff0c;而说到储存层&#xff0c;避免不了说到数据库&#xff0c;在我们面试的时候&#xff0c;数据库的知识题目占比是非常多的&#xff1a; 1.…...

使用 PowerShell 将 Excel 中的每个工作表单独另存为独立的文件

导语&#xff1a;在日常工作中&#xff0c;我们经常需要处理 Excel 文件。本文介绍了如何使用 PowerShell 脚本将一个 Excel 文件中的每个工作表单独另存为独立的 Excel 文件&#xff0c;以提高工作效率。 1. 准备工作 在开始之前&#xff0c;请确保已经安装了 Microsoft Exc…...

python提取pdf图片

import fitz import re import osdef save_pdf_img(path, save_path):path: pdf的路径save_path : 图片存储的路径# 使用正则表达式来查找图片checkXO r"/Type(? */XObject)"checkIM r"/Subtype(? */Image)"# 打开pdfdoc fitz.open(path)# 图片计数im…...

Vue3 表单输入绑定简单应用

去官网学习→表单输入绑定 | Vue.js 运行示例&#xff1a; 代码&#xff1a;HelloWorld.vue <template><div class"hello"><h1>Vue 表单输入绑定</h1><input type"text" placeholder"输入框" v-model"msg"…...

如何解决 Elasticsearch 查询缓慢的问题以获得更好的用户体验

作者&#xff1a;Philipp Kahr Elasticsearch Service 用户的重要注意事项&#xff1a;目前&#xff0c;本文中描述的 Kibana 设置更改仅限于 Cloud 控制台&#xff0c;如果没有我们支持团队的手动干预&#xff0c;则无法进行配置。 我们的工程团队正在努力消除对这些设置的限制…...

近期学习练习

练习&#xff1a; 或&#xff1a; //sqrt是数学库函数&#xff0c;开平方 //头文件为math.h 或...

平台安全之中间件安全

理解中间件 一次web访问的顺序&#xff0c;web浏览器->web服务器&#xff08;狭义&#xff09;->web容器->应用服务器->数据库服务器 web服务器 广义&#xff1a;提供广义web服务的软件或主机 狭义&#xff1a;提供w3服务的软件或主机&#xff0c;即Web服务器软件…...

芒果 TV 基于 Flink 的实时数仓建设实践

公司简介&#xff1a;芒果 TV 作为湖南广电旗下互联网视频平台&#xff0c;在“一云多屏&#xff0c;多元一体”的战略指导下&#xff0c;通过内容自制&#xff0c;培植核心竞争力&#xff0c;从独播、独特走向独创&#xff0c;并通过市场化运作完成 A 轮、B 轮融资&#xff0c…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...