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

芝法酱学习笔记(0.3)——SpringBoot下使用mybatis做增删改查和报表

零、前言

书接上回,我们搭建了windows下的开发环境,并给出了一个hello world级别的多模块SpringBoot项目。
毕竟java后端开发,离不开数据库的操作,为方便后面内容的讲解,这里再做一期铺垫,core模块下新增一个core-mpenhance模块,该模块引入mybatis-plus,并对其进行一定规范封装。基于该模块,我们编写一些crud的业务代码。

一、软件安装

说到数据库操作,windows端我一般会装2个软件。powerdesigner和workbench。

1.1 powerdesigner

powerdesigner是一个数据库设计软件,下载地址可以去这位网友那里下载。关于powerdesigner的用法,织法先前的博客有讲解。

2.2 workbench

可以去这里下载。为什么我不说navcat,因为workbench是免费的,而且满足使用需求,何必再用需要破解的软件呢?尤其是在上市公司和国企的小伙伴。

二、构思一个业务

本次笔记,打算实现一个简化了的供应商货物管理系统。

2.1 供应商管理系统简介

在市场上,商品的流通中,有4个角色,厂商、中间商、零售商、用户。我们现在要做的就是中间商用的一个管理系统。
中间商从厂商进货到自己的仓库,然后通过自己的业务员,把货物卖向一个个的零售商。

2.2 这次开发的核心系统

2.2.1 商品定义系统

定义一个个的商品,商品包括名称,商品单位转换比,商品所属的大类型、小类型和品牌等。
这里比较不好理解的是商品的转换比,我在这里多讲一些。
商品的单位类型有3种,如下枚举所示

@RequiredArgsConstructor
@EnumDesc(name = "商品单位类型",desc = "商品单位类型",defaultIdx = 2,defaultItem = "大小单位")
public enum EItemUnitType {SINGLE_UNIT(1,"单单位","只有一个小单位"),NORMAL_UNIT(2,"大小单位","有一个大单位,有一个小单位"),TRIPLE_UNIT(3,"三单位","有一个大单位,中单位,小单位");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;
}

比如最常见的NORMAL_UNIT,就是一个大单位一个小单位,比如1箱=24袋,箱就是大单位,袋是小单位,转换比是24.
在业务员录单时,一般录入大单位价格。统计销售量,也通常会统计大单位数量。
商品定义表设计如图所示:
在这里插入图片描述

2.2.2 商品录单

该系统的核心业务,业务员去门店销售商品。由于这里是学习贴,会做很多简化,先不去考虑预售等情况。
销售单一共3个类型,如下枚举:

@RequiredArgsConstructor
@EnumDesc(name = "销售单类型",desc = "销售单类型",defaultIdx = 1,defaultItem = "销售单")
public enum EConsignType {CONSIGN(1,"销售单","销售单,该单据从中间商出售商品到门店"),RETURN(2,"退货单","该单据从门店退货到中间商"),EXCHANGE(3, "换货单","换货单,门店把不好的货物退给中间商,拿到一个新的货物,相当于一个销售一个退货");@EnumValue@Getterprivate final int code;@Getterprivate final String name;@Getterprivate final String desc;
}

商品录单的数据结构,一个头表,一个明细表。一个销售单,可能包括多个商品的销售。数据库设计如图所示:
在这里插入图片描述

2.2.3 库存数据

在门店的商品是有库存概念的。销售时,销售数量不能超过库存。本次由于简化,就不再引入进货单的概念了,仅仅记录商品的库存和当前的成本价格。
商品的价格,取决于每次进货时的数量与价格。这种价格叫移动加权平均。比如原先库存中100个大单位,原价100元;再进货100个大单位,单价为150。此时,该商品的成本价格为125每个大单位。
库存表设计如图所示:
在这里插入图片描述

2.2.4 销售报表

销售报表中,需要根据不同维度分组。如商品Id,品牌,门店,业务员。也有复合分组,门店+商品。在查询时,会有时间范围,商品名、业务员名、门店等筛选条件。
销售报表的返回内容,无非是销售的数量、销售额、成本、利润等信息。并且支持按某种维度排序。

三、代码结构

3.1 mp-enhence

在core模块下,建一个mp-enhence的子模块,用于放置mysql相关操作的库。
由于该业务既有增删改查,又有报表相关的复杂连表分组查询,我们ORM选用mybatis-plus。
该模块如图所示:
在这里插入图片描述
由于该模块的实现,在之前的博客中已经写过,这里就不再赘述
芝法酱躺平攻略(3)—— 搭建基于mybatis-plus的开发框架
芝法酱躺平攻略(4)—— powerdesigner与mybatis-plus生成代码

3.2 代码结构的思考

大概讲一下代码位置的规划。分层设计虽然是一个看上去很干净的设计,但在实际开发中,不同模块间总会有相互关联,很难把每个模块拆成一个个的微服务。通常情况下,我们仅仅会针对大功能做服务划分。并且,微服务的拆分,其实完全可以体现在controller上,基层的业务代码,通过包引用方式引入。不同微服务仅仅是暴露不同的接口。
这样的情况下,现在的实际开发中,代码就会更偏向单体风格。然后有些程序员理解不深刻,硬要把代码做成分层的风格,比如所有的po在一个包内,所有service在一个包内,所有的mapper在一个包内。报表的mybatis的xml再放一个文件夹中。刚开始开发觉得没什么,但当业务量膨胀后,就会发现这样的结构写起代码十分痛苦。一个模块想找一下他相关的类,简直远隔千山万水。同时也使得,在实际执行时,基础程序员倾向于写更少的类。一个结构体里包含多个功能的字段,然后代码就成了屎山,极难维护。
这里展示一下我们这次的代码结构:
在这里插入图片描述
我们详细看一下报表系统的结构。我们可以看到,entity写了好多,分为request,response,mapperIn,mapperOut。
这里一定不要嫌烦,这种做法可以使代码结构清晰,并且易于复用。mapper的入参出参,一定要和controller的分开,不然代码会变得很不好理解。那还不如全用Map传递了事。既然都用map传递了,为什么不放弃Java改用python写代码呢?
具体可以在最后的代码展示中来看。
在这里插入图片描述

3.3 mybatis-plus还是原生mybatis

对于一般单表的增删改查业务,我更倾向于用mybatis-plus,减少不必要的代码量,快速开发。
而对于报表类的业务,会牵扯大量表连接,分组,聚合函数等情况。这时,使用原生的mybatis。

3.4 报表代码的复用性

由于该报表会有各个维度的分组查找,不同维度的查找返回字段也不相同。如果针对每个函数写一套xml的sql,那实在太麻烦了。毕竟mybatis支持动态sql。要不要查询商品相关信息,如何分组等,完全可以在mapper的入参中体现。
如何设置ConsignMapperIn,这个代码可以放在Request的类里。由于多数设置(如分页、时间范围等)也是通用的,所以可以写在Request的积累里。
我这里展示一部分代码,供大家参考

BaseConsignReportRequest

public abstract class BaseConsignReportRequest extends BasePageRequest {Long itemId;String itemName;String mainType;String segmentType;String brand;Long customerId;String customerName;Long salesId;String salesName;LocalDateTime timeBegin;LocalDateTime timeEnd;EConsignReportOrderType consignOrderType;EOrderType orderType;public BaseConsignReportRequest(){timeBegin = LocalDateTime.now().minusDays(3l);timeEnd = LocalDateTime.now();consignOrderType = EConsignReportOrderType.BILL_TIME;orderType = EOrderType.DESC;}public  ConsignMapperIn toMapperIn(){ConsignMapperIn consignMapperIn = new ConsignMapperIn();consignMapperIn.setItemId(itemId);consignMapperIn.setItemName(itemName);consignMapperIn.setMainType(mainType);consignMapperIn.setSegmentType(segmentType);consignMapperIn.setBrand(brand);consignMapperIn.setCustomerId(customerId);consignMapperIn.setCustomerName(customerName);consignMapperIn.setSalesId(salesId);consignMapperIn.setSalesName(salesName);consignMapperIn.setConsignOrderType(consignOrderType);consignMapperIn.setOrderType(orderType);Instant instantBeg = timeBegin.toInstant(ZoneOffset.UTC);consignMapperIn.setBillTimeBegin(instantBeg.getEpochSecond());Instant instantEnd = timeEnd.toInstant(ZoneOffset.UTC);consignMapperIn.setBillTimeEnd(instantEnd.getEpochSecond());if(size > 0){consignMapperIn.setPage(true);consignMapperIn.setOffset((current-1)*size);consignMapperIn.setLimit(size);}else{consignMapperIn.setPage(false);}consignMapperIn = onConsignMapperInInit(consignMapperIn);return consignMapperIn;}protected abstract ConsignMapperIn onConsignMapperInInit(ConsignMapperIn pConsignMapperIn);}

ConsignMapperIn

@Data
public class ConsignMapperIn {String itemName;Long itemId;String mainType;String segmentType;String brand;Long customerId;String customerName;Long salesId;String salesName;Long billTimeBegin;Long billTimeEnd;boolean selectItemInfo;boolean selectCustomerInfo;boolean selectSalesInfo;boolean isGroup;List<String> groups;boolean joinItem;boolean groupItem;EConsignReportOrderType consignOrderType;EOrderType orderType;boolean isPage;Integer offset;Integer limit;public ConsignMapperIn(){isGroup = true;groups = new ArrayList<String>();joinItem = false;consignOrderType = EConsignReportOrderType.BILL_TIME;orderType = EOrderType.NONE;isPage = true;selectItemInfo = false;selectCustomerInfo = false;selectSalesInfo = false;}
}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="indi.zhifa.study2024.class002.busy.generalTest.business.report.consign.mapper.ConsignMapper"><sql id="select_col">SUM(CASE WHEN cd.sale_type = 1 or cd.sale_type = 0 THEN cd.cost ELSE 0  END) AS sale_cost,SUM(CASE WHEN cd.sale_type = 1 THEN cd.amount ELSE 0 END) AS sale_amount,SUM(CASE WHEN cd.sale_type = 2 THEN -cd.cost ELSE 0  END) AS return_cost,SUM(CASE WHEN cd.sale_type = 2 THEN -cd.amount ELSE 0 END) AS return_amount,SUM(cd.amount) AS amount,SUM(cd.amount - cd.cost ) AS profit,SUM(cd.cost) AS cost,SUM(cd.amount - cd.cost ) / SUM(cd.amount) AS profit_ratio<if test="In.groupItem">,SUM(CASE WHEN cd.sale_type = 1 or cd.sale_type = 0 THEN cd.count ELSE 0  END) AS sale_cnt,SUM(CASE WHEN cd.sale_type = 2 THEN -cd.count ELSE 0  END) AS return_cnt,SUM(cd.count) AS nest_cnt</if><if test="not In.isGroup">,ch.id, ch.bill_no, ch.bill_type, ch.bill_time</if><if test="In.selectItemInfo">,cd.item_id, cd.item_name<if test="In.joinItem">,itm.unit_type,itm.small_unit_name,itm.mid_unit_name,itm.large_unit_name,itm.large_convert,itm.mid_convert,itm.main_type,itm.segment_type,itm.brand</if></if><if test="In.selectCustomerInfo">,ch.customer_id,ch.customer_name</if><if test="In.selectSalesInfo">,sales_id,sales_name</if></sql><select id="consignReport"resultType="indi.zhifa.study2024.class002.busy.generalTest.business.report.consign.entity.mapperOut.ConsignMapperOut">SELECT <include refid="select_col"/>FROMconsign_head ch JOIN consign_detail_simple cd ON ch.id = cd.bill_id<if test="In.joinItem">JOIN item_define itm ON cd.item_id = itm.id</if><where>ch.bill_time BETWEEN #{In.billTimeBegin} AND #{In.billTimeEnd}<if test="In.itemName != null and In.itemName != ''">AND cd.item_name like CONCAT('%', #{In.itemName}, '%')</if><if test="In.itemId != null">AND cd.item_id = #{In.item_id}</if><if test="In.mainType != null and In.mainTyp != '' and In.joinItem">AND itm.main_type = #{In.mainTyp}</if><if test="In.segmentType != null and In.segmentType != '' and In.joinItem">AND itm.segment_type = #{In.segmentType}</if><if test="In.brand != null and In.brand != '' and In.joinItem">AND itm.brand = #{In.brand}</if><if test="In.customerId != null">AND ch.customer_id = #{In.customerId}</if><if test="In.customerName != null and In.customerName != ''">AND ch.customer_name like CONCAT('%', #{In.customerName}, '%')</if><if test="In.salesId != null">AND ch.sales_id = #{In.salesId}</if><if test="In.salesName != null and In.salesName != ''">AND ch.sales_name = #{In.salesName}</if></where><if test="In.isGroup">GROUP BY<foreach item="item" index="index" collection="In.groups" separator=",">${item}</foreach></if><if test="In.orderType.name() != 'NONE'">ORDER BY ${In.consignOrderType.getKey()} ${In.orderType.getKey()}</if><if test="In.isPage">LIMIT #{In.offset},#{In.limit}</if></select>
</mapper>

四、出现的一些问题

4.1 分组问题

按照我写的这个sql,在不经任何设置时,就会报error 1055的sql错误。
这时,我们在workbench中先运行这个命令

SELECT @@sql_mode

而后,我们把返回值中的ONLY_FULL_GROUP_BY去掉
而后,进入linux的/etc/mysql
vim my.cnf
在最下面加这一段

sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

4.2 mybatis的xml的位置配置

首先在pom的build标签中,加入如下配置:
该配置的目的,是把mybatis的xml,放到mybatis的目录下

<build><resources><resource><directory>src/main/java/indi/zhifa/study2024/class002/busy/generalTest/business/report</directory><includes><include>**/*.xml</include></includes><targetPath>${project.build.directory}/classes/mybatis</targetPath></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources></build>

点击编译,观察target文件夹
在这里插入图片描述

在application.yml中,加入如下配置

mybatis-plus:mapper-locations: classpath:mybatis/**/xml/*.xml

这样一来,我们就不需要把xml写到一个文件夹下,可以和mapper放在一起。

4.3 打印sql

在mybatis-plus下加一段配置即可

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

五、代码展示

我们这节属于过渡章节,仅仅是不想让测试项目只有hello world,显得太过单薄。所以代码讲解并不是本章重点。所以,这里还是展示我的码云,大家可以下载后观看。

相关文章:

芝法酱学习笔记(0.3)——SpringBoot下使用mybatis做增删改查和报表

零、前言 书接上回&#xff0c;我们搭建了windows下的开发环境&#xff0c;并给出了一个hello world级别的多模块SpringBoot项目。 毕竟java后端开发&#xff0c;离不开数据库的操作&#xff0c;为方便后面内容的讲解&#xff0c;这里再做一期铺垫&#xff0c;core模块下新增一…...

windows msys2 编译x264 32位动态库

一、打开mingw32 查看gcc版本 gcc --version 提示找不到gcc&#xff0c;可以安装gcc pacman -S gcc 二、进入x264-master目录 cd /d/x264-master 执行 ./configure --prefix/d/x264-master/Bin --disable-asm --enable-static --enable-shared --disable-thread其中--disa…...

【pytorch】relu的实现逻辑

笔者最近在尝试实现AlexNet的底层算子&#xff0c;基于pytorch的框架&#xff0c;本文主要记录一下pytorch中是如何实现relu算子的。 首先最外层是位于torch\nn\modules\activation.py&#xff0c;主要代码如下&#xff1a; __constants__ ["inplace"]inplace: bool…...

【Python篇】深入机器学习核心:XGBoost 从入门到实战

文章目录 XGBoost 完整学习指南&#xff1a;从零开始掌握梯度提升1. 前言2. 什么是XGBoost&#xff1f;2.1 梯度提升简介 3. 安装 XGBoost4. 数据准备4.1 加载数据4.2 数据集划分 5. XGBoost 基础操作5.1 转换为 DMatrix 格式5.2 设置参数5.3 模型训练5.4 预测 6. 模型评估7. 超…...

简单学习 原码反码补码 学会了你才是真正的程序员了

一、简单介绍原码反码补码 首先我们需要知道的是原码反码补码是一个人为的行为&#xff0c;因为机器看的都是所谓的补码&#xff0c;这个反码只是作为补码的到原码也就是人能看懂的跳板&#xff0c;所以计算机无论是计算器里面的东西还是他底层运行的二进制代码都是补码&#x…...

基于规则的命名实体识别

基于规则的命名实体识别&#xff08;Rule-Based Named Entity Recognition, NER&#xff09;是一种通过预定义的模式或规则来识别文本中特定实体的方法。这种方法通常使用正则表达式来匹配文本中的实体。下面是一个更完整的示例&#xff0c;展示了如何使用正则表达式来识别文本…...

C语言从头学63—学习头文件stdlib.h(二)

6、随机数函数rand() 功能&#xff1a;产生0~RAND_MAX 之间的随机整数。 使用格式&#xff1a;rand(); //无参 返回值&#xff1a;返回随机整数 说明&#xff1a; a.RAND_MAX是一个定义在stdlib.h里面的宏&#xff0c;表示可以产生的最大随机整数&am…...

js判断一个对象里有没有某个属性

1. 使用in操作符 in操作符可以用来检测属性是否存在于对象或其原型链中。 const obj {a: 1, b: 2}; if (a in obj) { console.log(属性a存在于obj中); } else { console.log(属性a不存在于obj中); } 2. 使用hasOwnProperty()方法 hasOwnProperty()方法用来检测一个…...

Python(爬虫)正则表达式

正则表达式是文本匹配模式&#xff0c;也就是按照固定模式匹配文本 一、导入 re模块是Python环境的内置模块&#xff0c;所以无需手动安装。直接在文件中导入即可&#xff1a; import re 二、正则表达式基础知识 . 匹配除换行符以外的任意字符 ^ 匹配字符串的开始 $ 匹配字…...

Linux:进程(二)

目录 一、cwd的理解 二、fork的理解 1.代码共享 2.各司其职 3.fork的返回值 三、进程状态 1.进程排队 2.进程状态 运行状态 阻塞状态 挂起状态 一、cwd的理解 cwd&#xff08;current working directory&#xff09;。译为当前工作目录。 在C语言中&#xff0c;使用…...

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第二篇-着色器制作】

在上一篇文章中&#xff0c;我们已经理顺了实现流程。 接下来&#xff0c;我们将在UE5中&#xff0c;从头开始一步一步地构建一次流程。 通过这种方法&#xff0c;我们可以借助一个熟悉的开发环境&#xff0c;使那些对着色器不太熟悉的朋友们更好地理解着色器的工作原理。 这篇…...

【OceanBase 诊断调优】—— GC问题根因分析

GC 流程涉及到 RS 的状态切换和 LS 的资源安全回收&#xff0c;流程上较长。且 GC 线程每个租户仅有一个&#xff0c;某个日志流 GC Hang 死时会卡住所有其余日志流的 GC&#xff0c;进而造成更大的影响。 本文档会帮助大家快速定位到 GC 故障的模块&#xff0c;直达问题核心。…...

图像面积计算一般方法及MATLAB实现

一、引言 在数字图像处理中&#xff0c;经常需要获取感兴趣区域的面积属性&#xff0c;下面给出图像处理的一般步骤。 1.读入的彩色图像 2.将彩色图像转化为灰度图像 3.灰度图像转化为二值图像 4.区域标记 5.对每个区域的面积进行计算和显示 二、程序代码 %面积计算 cle…...

指挥平台在应急场所中的主要表现有哪些

在应对自然灾害、公共安全事件等突发危机时&#xff0c;指挥平台作为应急管理体系的核心枢纽&#xff0c;其重要性不言而喻。它不仅承载着信息的快速汇聚、精准分析与高效调度功能&#xff0c;更在应急场所中有一定的关键表现。接下来就跟着北京嘉德立一起了解一下。 一、信息集…...

智能养殖场人机交互检测系统源码分享

智能养殖场人机交互检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…...

数据集-目标检测系列-海洋鱼类检测数据集 fish>> DataBall

数据集-目标检测系列-海洋鱼类检测数据集 fish>> DataBall 数据集-目标检测系列-海洋鱼类检测数据集 fish 数据量&#xff1a;1W 数据项目地址&#xff1a; gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview github: https://github.com/…...

网络威慑战略带来的影响

文章目录 前言一、网络威慑的出现1、人工智能带来的机遇二、网络空间的威慑困境1、威慑概念的提出2、网络威慑的限度3、人类对网络威胁的认知变化4、网络空间的脆弱性总结前言 网络威慑是国家为应对网络空间风险和威胁而采取的战略。冷战时期核威慑路径难以有效复制至网络空间…...

决策树算法在机器学习中的应用

决策树算法在机器学习中的应用 决策树&#xff08;Decision Tree&#xff09;算法是一种基本的分类与回归方法&#xff0c;它通过树状结构对数据进行建模&#xff0c;以解决分类和回归问题。决策树算法在机器学习中具有广泛的应用&#xff0c;其直观性、易于理解和实现的特点使…...

Leetcode面试经典150题-39.组合总数进阶:40.组合总和II

本题是扩展题&#xff0c;真实考过&#xff0c;看这个题之前先看一下39题 Leetcode面试经典150题-39.组合总数-CSDN博客 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数…...

ProcessOn为什么导出有水印!!!(利用SVG转PNG)

processon-svg2png ProcessOn 一个非常好用的思维导图网站&#xff0c;但是为什么导出有水印&#xff01;&#xff01;&#xff01;。 功能 支持按钮拖拽支持将流程图svg 转成 png下载支持修改自定义文字下载svg&#xff08;开发中&#xff09; 安装/使用方法 安装并使用…...

BG3ModManager:博德之门3模组管理终极解决方案

BG3ModManager&#xff1a;博德之门3模组管理终极解决方案 【免费下载链接】BG3ModManager A mod manager for Baldurs Gate 3. This is the only official source! 项目地址: https://gitcode.com/gh_mirrors/bg/BG3ModManager 你是否曾经为《博德之门3》的模组管理而烦…...

开源智能体技术解析:从LangChain到自主抓取,构建自动化工作流

1. 项目概述&#xff1a;从“Awesome”列表看开源智能体生态的演进 最近在梳理一些前沿的自动化工具链时&#xff0c;又翻到了 mergisi/awesome-openclaw-agents 这个仓库。对于长期关注AI Agent&#xff08;智能体&#xff09;和自动化工作流开发的同行来说&#xff0c;这类…...

对比直接使用厂商 API 体验 Taotoken 在路由容灾上的价值

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比直接使用厂商 API 体验 Taotoken 在路由容灾上的价值 在开发依赖大模型能力的应用时&#xff0c;服务的连续性与稳定性是保障用…...

告别Demo!用EMQX和Java模拟真实物联网设备上报数据流(Windows本地开发环境)

告别Demo&#xff01;用EMQX和Java构建真实物联网数据流模拟方案 在物联网开发中&#xff0c;最令人头疼的莫过于缺乏真实设备进行测试。想象一下&#xff0c;当你精心设计的平台等待设备接入时&#xff0c;硬件团队却告诉你"下周才能交付原型机"。这种等待不仅拖延进…...

Linuxbonding链路稳定性治理方法

Linuxbonding链路稳定性治理方法这是一篇面向中级 Linux 使用者的技术文章&#xff0c;主题聚焦在bonding链路&#xff0c;重点讨论链路聚合、冗余切换和接口状态。在真实生产环境中&#xff0c;bonding链路相关问题往往不会以单一错误形式出现&#xff0c;而是混杂在日志、权限…...

从XTR文件看GNSS数据质量:如何利用Anubis报告优化你的测量方案(以GPS/BDS/Galileo为例)

从XTR文件解码GNSS数据质量&#xff1a;实战分析与优化策略 在GNSS测量领域&#xff0c;数据质量直接决定了最终定位结果的可靠性。XTR文件作为Anubis软件生成的质量报告&#xff0c;包含了大量反映GNSS观测质量的指标参数。对于有经验的工程师而言&#xff0c;这些数字不仅仅是…...

开源技能库构建指南:Git+Markdown+Docsify打造个人技术知识体系

1. 项目概述&#xff1a;一个开源技能库的诞生与价值在技术领域&#xff0c;尤其是软件开发、运维和数据分析等方向&#xff0c;我们每天都在与海量的工具、框架和命令打交道。时间一长&#xff0c;一个很现实的问题就摆在了面前&#xff1a;那些曾经花了好几个小时才调通的复杂…...

GitClaw:基于Go的轻量级Git钩子服务器与集中式权限管理方案

1. 项目概述与核心价值如果你是一名开发者&#xff0c;尤其是经常在团队协作中处理Git仓库的工程师&#xff0c;那么你一定对“权限管理”这四个字又爱又恨。爱的是它能保障代码安全&#xff0c;恨的是它配置起来繁琐&#xff0c;尤其是在处理跨项目、跨团队的复杂权限矩阵时。…...

Iris API错误处理机制与嵌入式系统优化实践

1. Iris API错误处理机制解析在嵌入式系统开发中&#xff0c;API的健壮性直接影响整个系统的稳定性。Iris框架作为ARM架构下的核心组件&#xff0c;其错误处理机制基于JSON-RPC 2.0规范进行了深度定制&#xff0c;特别适合资源受限的嵌入式环境。与通用Web API不同&#xff0c;…...

基于BLE HID与旋转编码器打造双模式无线遥控器

1. 项目概述你有没有过这样的时刻&#xff1a;窝在沙发里看剧&#xff0c;想调个音量或者暂停一下&#xff0c;却不得不伸手去够茶几上的键盘或鼠标&#xff0c;打断那份沉浸的惬意&#xff1f;或者&#xff0c;在电脑上回味一些经典老游戏时&#xff0c;觉得用键盘移动、鼠标射…...