Compare commits

...
11 Commits
Author SHA1 Message Date
张育新 a2dbd51f35 Merge branch 'main' of https://github.com/OSHMKUFA5100/lab
# Conflicts:
#	note/随手记/go.md
2026-08-06 09:15:15 +08:00
张育新 241a7c43da docs(note): 新增 excel/正则/Nginx 笔记并整理目录
- 新增 AGENTS.md 仓库说明与 excelize 依赖
- 新增正则表达式、go操作excel、Nginx常见错误、Golang底层架构笔记
- 布尔变量命名迁移至资源/编程/规范,删除随手记旧笔记
2026-08-05 18:32:17 +08:00
张育新 735a339deb test(regularMatch): 添加中文正则匹配测试 2026-08-05 18:31:59 +08:00
labmem b6063cb9f9 docs(go): 添加内存逃逸学习笔记与示例代码
- 新增 go/basics/MemoryEscape/main.go,演示返回局部变量指针和闭包引用两种典型逃逸场景
- 在 note/随手记/go.md 中补充内存逃逸的概念、影响、常见原因及逃逸分析命令
- 清理 note/随手记/网址.md 中的多余空行
2026-07-23 00:07:16 +08:00
张育新 ef18635509 docs(note): 补充技术笔记和主题配置
- Go map Swiss Table 实现说明
- MarkText 仿 onelight 暗色主题
- 布尔变量命名规范
- 更新网址收藏及跟踪 assets 资源
2026-07-21 17:53:15 +08:00
张育新 3819ae9d94 Merge branch 'main' of https://github.com/OSHMKUFA5100/lab 2026-07-15 19:14:20 +08:00
张育新 e486d47e2a docs(note): reorganize notes structure and migrate content
- Move Windows and 算法 notes from 随手记 to 资源 directory
- Migrate Golang map pitfalls and dot import notes to Golang/使用注意
- Delete omos配置 note
- Add Typora custom theme reference link
2026-07-15 19:12:07 +08:00
labmem e6fe90a7c0 Merge branch 'main' of https://github.com/OSHMKUFA5100/lab 2026-07-13 23:12:38 +08:00
张育新 e00245ea66 docs(note): 补充 git merge --abort 取消合并命令 2026-07-13 22:46:30 +08:00
张育新 f7f5d379a9 test(testing): 添加中位数计算和通道通信测试用例
- 新增 TestNormal: 测试 GetMiddNumber 中位数计算逻辑
- 新增 TestChannel: 模拟通道传球 goroutine 交互
2026-07-13 22:46:07 +08:00
张育新 be711334fb docs(note): 添加笔记目录及 gitignore 规则
- 新增 note 目录包含 AI、SQL、Golang、网络等多领域学习笔记
- 更新 .gitignore 添加 note/assets/ 和 note/项目/ 忽略规则
2026-07-10 09:24:40 +08:00
35 changed files with 4585 additions and 1 deletions
+4 -1
View File
@@ -27,4 +27,7 @@ build/
# ---- 操作系统 ----
.DS_Store
Thumbs.db
Thumbs.db
# ---- Note 目录 ----
note/项目/
+36
View File
@@ -0,0 +1,36 @@
# AGENTS.md
## 仓库结构
按语言分层的个人练习仓库,每种语言一个顶层目录,各自独立管理依赖。
| 目录 | 说明 |
|---|---|
| `go/` | Go module `lab`(Go 1.26.2),go.mod 在该目录内 |
| `note/` | 个人笔记(非代码),`note/项目/` 被 gitignore |
| `dart/` | 预留,尚未建立 |
## Go 约定
- **Module 在 `go/` 目录下,不在仓库根**——所有 go 命令需在 `go/` 内执行
- 每个叶子目录 = 一个独立小程序(`package main` + `main.go`)
- 多个小程序可并存,不同目录、各自独立
- 主题目录(basics/concurrency/stdlib/patterns/algorithms/web/testing/project)可随时增删
- 测试文件用 `*_test.go`,运行单个测试:`cd go && go test ./testing/ -run TestSliceAppend`
## 开发命令
```
# Go——所有命令在 go/ 目录下执行
cd go
go run ./<主题>/<程序名>/ # 运行单个程序
go test ./... # 运行全部测试
go test ./testing/ -v # 运行 testing 包测试
go mod tidy # 整理依赖
```
## 注意事项
- 仓库无 CI、无 pre-commit hooks、无代码检查——每个程序自行保证可编译即可
- 笔记和练习代码严格分离:程序相关说明写在程序目录的 README.md,系统性知识放 `note/`
- `.gitignore` 已覆盖 Go 二进制、Dart/Flutter 构建产物、IDE 配置、操作系统文件
+25
View File
@@ -0,0 +1,25 @@
package main
import (
"fmt"
)
type User struct {
Name string
}
func NewUser() *User {
u := User{}
return &u
}
func ClosureFunc() func() int {
a := 1
return func() int {
return a
}
}
func main() {
fmt.Println(NewUser())
cf:= ClosureFunc()
fmt.Println(cf())
}
+12
View File
@@ -1,3 +1,15 @@
module lab
go 1.26.2
require (
github.com/richardlehane/mscfb v1.0.7 // indirect
github.com/richardlehane/msoleps v1.0.6 // indirect
github.com/tiendc/go-deepcopy v1.7.2 // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/excelize/v2 v2.11.0 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/text v0.38.0 // indirect
)
+18
View File
@@ -0,0 +1,18 @@
github.com/richardlehane/mscfb v1.0.7 h1:oeoiM0WE79vHwE8RpIYYvIAc8ajTH2mb6UZm55/+EB0=
github.com/richardlehane/mscfb v1.0.7/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.11.0 h1:HxaEFl6sRN2+8J5a8HaKq+0M4FsjBGMnWWtjOCPSG88=
github.com/xuri/excelize/v2 v2.11.0/go.mod h1:jxFLbzaIwGQ5ufFNvYfUOHqXhfPaNmP14KWfmNz2Uak=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
+48
View File
@@ -0,0 +1,48 @@
package testing
import (
"sort"
"testing"
"time"
)
func TestNormal(t *testing.T) {
numbers := map[int][]int{
1: {8, 2, 4, 4},
2: {0, 5, 3},
3: {1},
}
for _, numbers := range numbers {
middle := GetMiddNumber(numbers)
t.Log(middle)
}
}
func GetMiddNumber(numbers []int) float64 {
sort.Ints(numbers)
length := len(numbers)
isOddNUmber := length%2 == 1
if !isOddNUmber {
return float64(numbers[length/2-1]+numbers[length/2]) / 2.0
}
return float64(numbers[length/2])
}
func TestChannel(t *testing.T) {
var ball = make(chan string)
kickBall := func(playerName string) {
for {
t.Log(<-ball, "传球", "\n")
time.Sleep(time.Second)
ball <- playerName
}
}
go kickBall("张三")
go kickBall("李四")
go kickBall("王二麻子")
go kickBall("刘大")
ball <- "裁判" // 开球
var c chan bool // 一个零值nil通道
<-c // 永久阻塞在此
}
+21
View File
@@ -0,0 +1,21 @@
package regularmatch
import (
"regexp"
"testing"
)
func TestChineseRegExp(t *testing.T) {
reg, err := regexp.Compile("[\u4e00-\u9fa5]")
if err != nil {
t.Errorf("Failed to compile regex: %v", err)
}
matchStrSlice := reg.FindAllString("hello 世界", -1)
if len(matchStrSlice) != 2 {
t.Errorf("Expected 2 matches, got %d", len(matchStrSlice))
}
if matchStrSlice[0] != "世" || matchStrSlice[1] != "界" {
t.Errorf("Expected [世, 界], got %v", matchStrSlice)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

+303
View File
@@ -0,0 +1,303 @@
# HTTP
## 上传文件
> **https://github.com/zeromicro/zero-examples/tree/main/http/upload**
### 前端
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<form
enctype="multipart/form-data"
action="http://localhost:8888/upload"
method="post"
>
<input type="file" name="myFile" />
<input type="submit" value="upload" />
</form>
</body>
</html>
```
> **参数说明**
- `enctype="multipart/form-data"` 用于支持文件的二进制传输
- `<input type="file">` 让用户挑选本地文件
- `action` 和 `method`,将数据以 `POST` 方式提交到后端地址
### 后端
```yaml
Name: upload-api
Host: 0.0.0.0
Port: 8888
MaxBytes: 1073741824
Timeout: 30000
```
> **yaml文件需要设置上传的最大字节,并且如果为大文件,需要考虑超时时间设置是否合理**
```go
func (l *UploadLogic) Upload(r *http.Request) (resp *types.Response, err error) {
err = r.ParseMultipartForm(10 << 27)
if err != nil {
fmt.Println("错误发生:", err)
return nil, err
}
file, handler, err := r.FormFile("myFile")
if err != nil {
fmt.Println(err)
return nil, err
}
defer file.Close()
fmt.Printf("Uploaded File: %+v\n", handler.Filename)
fmt.Printf("File Size: %+v\n", handler.Size)
fmt.Printf("MIME Header: %+v\n", handler.Header)
tempFile, err := os.Create(path.Join(".", handler.Filename))
if err != nil {
fmt.Println(err)
return nil, err
}
defer tempFile.Close()
io.Copy(tempFile, file)
return &types.Response{
OK: 0,
}, nil
}
```
> **API逻辑说明**
- 需要在handler里面将`*http.Request`传递到逻辑层
- `ParseMultipartForm`用于解析 `multipart/form-data` 类型的请求体
主要作用是:
​ 解析表单字段(包括文件上传字段)
​ 将请求体拆分成多个部分(parts),并填充到 `r.MultipartForm` 中
​ 内存与磁盘分配:最多在内存中保留 `maxFileSize` 字节,超出的部分会写入临时文件
- `FormFile(key string)` 从已解析的 `r.MultipartForm` 中提取名为 `"myFile"` 的文件部分
- `tempFile, err := os.Create(path.Join(".", handler.Filename))`将文件上传到当前目录
# DTM
## 基础概念
- **AP(Application)**:应用程序,发起全局事务请求。
- **TM(Transaction Manager)**:事务协调者,负责协调分布式事务的执行流程。
- **RM(Resource Manager)**:资源管理者,例如订单服务、库存服务等,执行具体业务与补偿逻辑
- **事务分支**:我们把每个服务管理的全局事务组成部分,称为事务分支,例如前面的转账,分为转出和转入两个事务分支
- **分支操作**:每个事务分支,在SAGA、XA、TCC等事务模式下,会有多个操作,例如转出事务分支,包括正向操作TransOut和TransOutCompensate
- **本地事务**:转出事务分支中的正向操作,通常会开启一个事务,对余额进行扣减,我们将数据操作的这个事务,称为本地事务
- **GID**:全局事务ID,用于标记全局事务,必须唯一。该ID可以采用UUID生成,也可以使用业务上的ID,例如订单ID等
| 模式 | 场景 | 典型使用 |
| ---------- | ---------------------------------------- | --------------------------------- |
| 二阶段消息 | 需要确保消息通知或缓存更新与本地事务同步 | 发短信/推送、消息投递、缓存一致性 |
| Saga | 流程复杂、长链路、不需强一致性 | 电商下单→扣库存→支付等微服务流程 |
| TCC | 强一致、高隔离资源场景 | 金融系统、库存冻结、大额支付 |
## 二阶段消息
是dtm首创的事务模式,用于替换本地事务表和事务消息这两种现有的方案。它能够保证本地事务的提交和全局事务提交是“原子的”,适合解决不需要回滚的分布式事务场景,比如发消息、更新缓存等
- **准备阶段**(Prepare) ,应用先发一个准备信号给 DTM Server,表示“我现在要做本地事务了”。DTM Server 接收到后先记录这个请求,但不触发后续通知。
- 执行本地事务, 应用开始执行本地事务(如写数据库、扣款),并在同一个数据库里,记录这次全局事务的 gid(全局事务 ID)。这保证了后续可以根据 gid 判断本地事务是否已成功。
- **提交阶段**(Submit), 本地事务成功后,应用再向 DTM 提交提交请求。DTM 执行这个操作后,才真正触发下游服务或消息投递。开始调用其他微服务或消息队列,实现完整的全局动作。
**优点:**
- 保证“本地事务 + 消息”业务要么同时成功,否则都不执行。(强触发保障)
- 对开发者透明,业务侵入小。
**缺点:**
- 需要开发一套 Prepare/Submit 调用,系统需定期监控、回查状态。
- 不适用于需要复杂补偿的场景。
## SAGA
核心思想是将长事务拆分为多个短事务,由Saga事务协调器协调,如果每个短事务都成功提交完成,那么全局事务就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。例如我们要进行一个类似于银行跨行转账的业务,将A中的30元转给B,根据Saga事务的原理,我们将整个全局事务,切分为以下服务:
- **转出**(TransOut)服务,这里转出将会进行操作A-30
- **转出补偿**(TransOutCompensate)服务,回滚上面的转出操作,即A+30
- **转入**(TransIn)服务,转入将会进行B+30
- **转入补偿**(TransInCompensate)服务,回滚上面的转入操作,即B-30
**优点:**
- 流程自然、开发简单,只要为操作实现补偿逻辑即可。
- 对资源无锁设计,支持高并发、长流程,性能佳。
**缺点:**
- 是最终一致性,存在临时中间状态(如库存已扣但未支付)。
- 无事务隔离,可能出现脏写或并发冲突。
- 补偿逻辑可能复杂易错(幂等、防重复)。
## TCC
TCC 是一种“预留-确认-取消”的事务模式,将事务分为三个阶段,高精度控制点 的场景,比如:支付系统、资金冻结、库存预扣等
- **Try** 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)
- **Confirm** 阶段:如果所有分支的Try都成功了,则走到Confirm阶段。Confirm真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源
- **Cancel** 阶段:如果所有分支的Try有一个失败了,则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。
**优点:**
- 强一致性:通过 Try 预留资源,Confirm/Cancel 明确执行或回滚,避免中间状态。
- 高并发友好,资源锁粒度小;性能优于传统 XA/2PC。
**缺点:**
- 业务侵入性高,每个参与方需实现三个操作(Try/Confirm/Cancel)。
- 实现复杂:需处理悬挂、空回滚、幂等、补偿重试等问题。
## SAGA实践
- 准备RM和DTM数据表
[GitHub地址](https://github.com/dtm-labs/dtm/tree/main/sqls)
- DTM配置
[GitHub地址](https://github.com/dtm-labs/dtm/blob/main/conf.sample.yml)
- docker部署参考配置
```yaml
LogLevel: 'info'
Server:
HttpPort: 36789 #HTTP port
GrpcPort: 36790 #Grpc port
Store:
Driver: "mysql" # 数据库类型
Host: "mysql" # 使用容器名
Port: 3306
User: "root"
Password: "root"
Db: "dtm"
MicroService:
Driver: "dtm-driver-gozero" #使用go-zero的注册服务发现驱动
Target: "etcd://etcd:2379/dtmservice" # 使用容器名,当前dtm的server直接注册到微服务所在的etcd集群中使用
EndPoint: "dtm:36790" # 使用容器名,集群中的微服务可以直接通过etcd获得此地址跟dtm交互
```
- go-zero的yaml参考配置
```yaml
#AP使用etcd的配置
Etcd:
Hosts:
- etcd:2379
Key: order.api
#RM使用etcd的配置
OrderRpc:
Etcd:
Hosts:
- etcd:2379
Key: order.rpc
#RM使用etcd的配置
StockRpc:
Etcd:
Hosts:
- etcd:2379
Key: stock.rpc
```
SAGA 事务编排使用步骤
```go
// dtm服务在etcd中的调用地址
var dtmServer = "etcd://localhost:2379/dtmservice"
// 从配置中通过BuildTarget获得对应RM的etcd调用地址
orderTarget, err := l.svcCtx.Config.OrderRpcConf.BuildTarget()
stockTarget, err := l.svcCtx.Config.StockRpcConf.BuildTarget()
// 构造对应的请求结构体,用于后面的本地事务的执行或者回滚补偿
createOrderReq := &order.CreateReq{UserId: req.UserId, GoodsId: req.GoodsId, Num: req.Num}
deductReq := &stock.DecuctReq{GoodsId: req.GoodsId, Num: req.Num}
// 使用dtmgrpc注册一个全局事务ID,用于标记全局事务
gid := dtmgrpc.MustGenGid(dtmServer)
// 开启全局事务,并提交
// dtm需要从dtm服务器调用该方法,所以不走强类型,而是走动态的url: busiServer+"/trans.TransSvc/TransOut"
// "/trans.TransSvc/TransOut"对应的是grpc_pb.go文件中Invoke方法所使用的路径
saga := dtmgrpc.NewSagaGrpc(dtmServer, gid).
Add(
orderTarget+"/pb.order/create",
orderTarget+"/pb.order/createRollback",
createOrderReq,
).
Add(
stockTarget+"/pb.stock/deduct",
stockTarget+"/pb.stock/deductRollback",
deductReq,
)
err = saga.Submit()
```
服务实现(包含子事务屏障)
```go
// 从 gRPC 请求上下文中提取分布式事务信息,构建一个用于管理子事务屏障的对象
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
// 获取一个sqlx的数据库连接
db, err := sqlx.NewMysql(l.svcCtx.Config.DB.DataSource).RawDB()
if err != nil {
// 一般数据库不会错误不需要dtm回滚,就让他一直重试
// 返回为status.Error(codes.Internal, err.Error()),会让dtm一直重试,不会回滚,直到
// 返回为status.Error(codes.Aborted, dtmcli.ResultFailure),会让dtm立刻调用回滚方法
return nil, status.Error(codes.Internal, err.Error())
}
// 开启本地事务(这里没写,正常应该在本地事务中再开启子事务屏障进行业务处理
// 将业务逻辑包裹在子事务屏障的保护下执行
if err := barrier.CallWithDB(db, func(tx *sql.Tx) error {
order := new(model.Order)
order.GoodsId = in.GoodsId
order.Num = in.Num
order.UserId = in.UserId
_, err = l.svcCtx.OrderModel.Insert(tx, order)
if err != nil {
return fmt.Errorf("创建订单失败 err : %v , order:%+v \n", err, order)
}
return nil
}); err != nil {
// 同上,让dtm一直重试,不回滚
return nil, status.Error(codes.Internal, err.Error())
}
// 补偿操作跟上面差不多,就是业务逻辑修改
```
+70
View File
@@ -0,0 +1,70 @@
​
## oh-my-openagent
### Agent角色
| **Agent 名称** | **属性** | **一句话特点** |
| ----------------- | -------- | ------------------------------------------------------------ |
| Sisyphus | 台前主控 | 项目总管、日常聊天与轻量开发入口,居中调度,维持记忆的多面手 |
| Prometheus | 台前主控 | 复杂需求的规划者、Gap(差距)分析师,只出谋划策(写 Plan),不动手写代码 |
| Atlas | 台前主控 | 流水线执行者、项目经理,盯着 TODO 督战的施工队包工头 |
| Hephaestus | 台前主控 | 火神、地狱级 Bug 终结者,单兵作战,不计成本死磕疑难杂症 |
| Oracle | 幕后工具 | 代码审查员、安全顾问,挑刺、CR、架构安全性把关 |
| Momus | 幕后工具 | Debug 专家,专门吃报错日志并给出修复建议 |
| Metis | 幕后工具 | 质检员、对齐审计,检查代码跟计划对不对齐,防止跑偏 |
| Sisyphus-junior | 幕后工具 | 基层打工人、代码搬砖牛马,真正动手改文件的“牛马码农” |
| Explore | 幕后工具 | 代码库检索员,在代码库里疯狂乱窜、扫图、找文件 |
| Librarian | 幕后工具 | 文档与知识检索员,专门翻文档、查第三方库用法 |
| Multimodal-looker | 幕后工具 | 视觉质检员,负责看截图、核对 UI 视觉问题 |
### 内置分类预设
| 分类名称(Category) | 预设心智与应用场景 |
| ------------------ | ------------------------------------------------------------ |
| visual-engineering | **像素级前端 UI 专家**。专门处理前端组件、页面样式美化、CSS 动画调整,默认偏向调度带有视觉能力的模型。 |
| deep | **目标导向的硬核攻坚战**。针对棘手难啃的底层问题,严格遵循“单次调用:一个目标 + 一个交付物”原则,死磕到底。 |
| quick | **低成本牛马杂活线**。处理单文件小改动、错别字修复、简单注释补充。默认绑定极便宜的 Mini 或 Flash 模型来帮你疯狂省钱。 |
| ultrabrain | 高浓度脑力推演 |
| writing | **散文与技术文档写手**。专门用来编写官方 Markdown 文档、技术方案白皮书或代码内说明。 |
## oh-my-opencode-slim
### 预设
`oh-my-opencode-slim.json`中的顶级字段`presets`为预设对象数组,默认设置了两套:一套是`openai`,一套是`opencode-go`。可以自行增删预设或者修改`agent`角色的模型。
```
# 在opencode会话中输入
/preset <name>
```
### Agent角色
| **Agent 名称** | **角色** | **模型选择** |
| -------------- | ----------------------- | ------------------------------------------------------------ |
| Orchestrator | 首席委派者和战略协调员 | 选择您的默认、最强的全能型编程模型。Orchestrator 既是核心编程智能体,又是委派者,因此它需要强大的实现能力、出色的判断力和可靠的指令遵循度。 |
| Explorer | 代码库侦察 | 选择快速、低成本的模型。Explorer 处理宽泛的侦察工作,因此速度 and 效率通常比使用最强推理模型更重要。 |
| Oracle | 战略顾问和终极调试者 | 选择您最强的高推理模型,用于架构设计、疑难调试、方案权衡以及代码审查。 |
| Council | 多LLM共识与提炼 | 使用一个强大的综合提炼模型作为 Council 智能体本身,并选择多样化的模型作为议员。Council 的价值在于对比不同的模型视角,而不仅仅是在所有地方都选择同一个最强的模型。 |
| Librarian | 外部知识检索 | 选择快速、低成本的模型。Librarian 处理调研和文档查询,因此速度和效率通常比使用最强推理模型更重要。 |
| Designer | UI/UX实现和极致视觉呈现 | 选择在 UI/UX 判断、前端实现和视觉打磨方面表现强劲的模型。 |
| Fixer | 快速实现专家 | 选择一个快速、可靠的编程模型来执行常规且范围明确的开发工作。Fixer 通常从 Orchestrator 接收具体的计划或受限的指令,非常适合高效执行诸如编写测试、更新测试和直接的代码更改等任务。 |
| Observer | 多模态处理 | 如果Orchestrator模型不是多模态模型,可以启用 Observer 来处理图像、屏幕截图、PDF 以及其他视觉文件。 |
### Skill分配
## 技能分配
控制每个代理人可以使用的技能`~/.config/opencode/oh-my-opencode-slim.json`(或`.jsonc`):
| 句法 | 意义 |
| ------------------- | ------------------------------- |
| `["*"]` | 所有已安装的技能 |
| `["*", "!codemap"]` | 除以下技能外的所有技能`codemap` |
| `["simplify"]` | 仅有的`simplify` |
| `[]` | 没有技能 |
| `["!*"]` | 拒绝所有技能 |
+70
View File
@@ -0,0 +1,70 @@
### 常用命令
#### 一、分支管理
| 操作 | 命令 | 说明 |
| -------------------- | -------------------------------------- | ---------------------------- |
| 查看本地分支 | `git branch` | 显示所有本地分支 |
| 查看远程分支 | `git branch -r` | 显示远程分支 |
| 创建新分支 | `git checkout -b feature/xxx` | 基于当前分支创建并切换新分支 |
| 基于远程创建本地分支 | `git checkout -b dev origin/dev` | 跟踪远程分支创建 |
| 删除本地分支 | `git branch -d feature/xxx` | 删除(已合并)本地分支 |
| 删除远程分支 | `git push origin --delete feature/xxx` | 删除远程分支 |
| 重命名分支 | `git branch -m old new` | 修改分支名 |
#### 二、提交相关
| 操作 | 命令 | 说明 |
| ------------------ | ------------------------------- | ------------------------------------------ |
| 添加到暂存区 | `git add .` 或 `git add <file>` | 添加改动文件 |
| 提交代码 | `git commit` | 使用文本编辑器编写提交信息后提交 |
| 提交代码 | `git commit -m "描述信息"` | 提交到本地仓库,可使用多个`-m`提交多行信息 |
| 修改上一次提交信息 | `git commit --amend` | 用于补充或改描述 |
| 查看提交历史 | `git log --oneline` | 简洁模式显示历史 |
#### 三、远程操作
| 操作 | 命令 | 说明 |
| ---------------- | --------------------------------------------------------- | ------------------- |
| 克隆仓库 | `git clone <url>` | 拉取远程项目 |
| 拉取远程代码 | `git pull` | 获取并合并远程分支 |
| 推送代码 | `git push` | 推送当前分支到远程 |
| 推送并设置上游 | `git push -u origin <branch>` | 建立跟踪关系 |
| 查看远程地址 | `git remote -v` | 显示远程仓库地址 |
| 远程查询所有标签 | git ls-remote --tags https://github.com/xxx.git \| grep X | 查看X相关的所有标签 |
#### 四、合并与变基
| 操作 | 命令 | 说明 |
| -------------- | ------------------------------------ | ------------------------------- |
| 合并分支 | `git merge feature/xxx` | 把目标分支合并进当前分支 |
| 取消合并 | `git merge --abort` | 终止合并,直接回退到合并前的状态 |
| 变基(rebase) | `git rebase dev` | 让你的提交在最新 `dev` 上 |
| 解决冲突后继续 | `git add . && git rebase --continue` | 继续变基或合并流程 |
#### 五、查看状态和差异
| 操作 | 命令 | 说明 |
| ---------------- | ------------------- | ---------------------------- |
| 查看状态 | `git status` | 当前分支、暂存区、工作区状态 |
| 查看修改内容 | `git diff` | 查看未暂存的更改 |
| 查看暂存内容差异 | `git diff --cached` | 查看已 add 的更改 |
#### 六、撤销操作
| 操作 | 命令 | 说明 |
| ----------------------- | ------------------------- | -------------------- |
| 撤销 add | `git reset <file>` | 把文件移出暂存区 |
| 撤销 commit(保留修改) | `git reset --soft HEAD~1` | 撤销最近一次提交 |
| 丢弃修改 | `git checkout -- <file>` | 丢弃对文件的本地更改 |
| 丢弃所有工作区修改 | `git reset --hard` | 恢复到最后一次提交 |
#### 七、其他常用
| 操作 | 命令 | 说明 |
| ---------------------- | ------------------------------------------------------------ | ---- |
| 查看当前分支 | `git branch --show-current` 或 `git rev-parse --abbrev-ref HEAD` | |
| 查看某次提交内容 | `git show <commit-id>` | |
| 切换到上一个分支 | `git checkout -` | |
| 清理无效引用 | `git remote prune origin` | |
| 从远程仓库获取所有标签 | `git fetch origin --tags` | |
+117
View File
@@ -0,0 +1,117 @@
[来源网站](https://www.conventionalcommits.org/zh-hant/v1.0.0/)
## 提交说明
### 基础结构
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
# 译文
<类型>[可选 范围]: <描述>
[可选 正文]
[可选 脚注]
```
### 类型(type)
- feat: 表示在代码库中新增了一个功能;
- fix: 表示在代码库中修复了一个 bug;
- build: 用于修改项目构建系统或外部依赖,例如升级依赖版本等;
- chore: 用于其他不修改`src`或`test`文件的杂项任务,例如更新`.gitignore`、修改编辑器配置等;
- ci: 用于修改持续集成/持续部署的配置与脚本,例如`GitHub Actions`、`Jenkins`、`Travis` 等工作流;
- docs: 用于修改文档,例如修改`README`文件、API文档等;
- style: 用于修改代码格式(不影响代码逻辑),例如调整缩进、空格、分号、引号风格等;
- refactor: 用于重构代码,例如修改代码结构、变量名、函数名等但不修改功能逻辑;
- improvement: 用于对现有实现进行改进,例如优化算法、增强健壮性等(不涉及新功能或 bug 修复);
- revert: 用于回退之前的提交,描述应使用被回退提交的标题,并在脚注中用 `Refs:` 引用被回退的 commit SHA;
- perf: 用于优化性能,例如提升代码的性能、减少内存占用等;
- wip: 用于保存尚未完成的工作进度(Work In Progress);
- test: 用于修改测试用例,例如添加、删除、修改代码的测试用例等;
> 破坏性变更**必须**在提交信息中标记出来,有两种方式:
>
> 1.在 `<类型>[可选 范围]` 后、冒号前添加 `!`,例如:`feat!: ...` 或 `refactor(api)!: ...`
>
> 2.*在脚注中添加* `BREAKING CHANGE: <描述>`
>
> 这两种方式可以同时使用,也可以只用其一。
### 范围(Scope)
范围用于标识本次修改影响的模块或组件,例如:
- feat(auth): ... (auth 模块)
- fix(parser): ... (parser 模块)
- build(deps): ... (依赖管理)
范围名称建议使用小写英文,多个单词用 `-` 连接,例如 `user-profile`。
### 描述(description)
描述用于简要概括本次变更的内容,要求:
- 使用祈使语气、现在时态(例如用 "add" 而非 "added" 或 "adds");
- 小写开头,末尾不加句号;
- 长度建议控制在50个字符以内(中文约 25 个字);
### 正文(Body)
正文是可选的,用于对本次变更进行更详细的说明,包括:
- 变更的动机(为什么这样做)
- 与之前行为的对比
- 实现方案的简要说明
正文与描述之间**必须**空一行,正文内部段落之间也可以空行分隔。
### 脚注(Footer)
脚注用于记录元信息,常见用法:
- `BREAKING CHANGE: <描述>` — 标记破坏性变更;
- `Closes #123` 或 `Fixes #456` — 关联 Issue/PR;
- `Refs: abc123` — 引用相关提交;
- `Reviewed-by: <name>` — 标记审查者;
- `Co-authored-by: <name> <email>` — 标记共同作者;
## 示例
### 基础功能提交
feat(auth): add JWT token refresh mechanism
### Bug 修复(含关联 issue)
fix(parser): handle empty string input edge case
Closes #123
### 破坏性变更(使用 ! 前缀)
feat(api)!: change user endpoint response format
BREAKING CHANGE: the `GET /api/users` endpoint now returns
a paginated response instead of a flat array.
### 回退提交
revert: remove experimental cache layer
Refs: a1b2c3d
+24
View File
@@ -0,0 +1,24 @@
### SSH
#### 快速连接服务器
在ssh的配置文件`~/.ssh/config`中可以设置常用的服务配置,这样就可以直接使用`ssh myserver`快速连接服务器了
```
Host myserver
HostName your-server.com
Port 2222
User ubuntu
IdentityFile ~/.ssh/mykey
```
#### 本地转发
```bash
# 使用示例
ssh -L 25432:localhost:5432 ubuntu@your-server.com
```
**作用**:以本地端口25432请求远程服务器,在远程服务器看来,这是以远程`localhost:5432`发起的请求
**使用场景**:当远程服务器某服务限制了只接收远程服务器的127.0.0.1连接,这时候又需要远程连接这个服务,这样的话就能和访问本地服务一样访问了
+84
View File
@@ -0,0 +1,84 @@
## 程序、进程与线程
### 程序
#### 定义
程序是存储在硬盘的一组指令集和,它是对计算任务的完整描述,是一个被动实体。类似于一本菜谱,记录了完整的烹饪步骤。
#### 核心特征
| 属性 | 说明 |
| :------------- | :-------------------------------------------------------- |
| **静态性** | 程序是一个文件(如 .exe、.go),不占用 CPU 或内存执行资源 |
| **持久性** | 可长期存在于存储介质上 |
| **无生命周期** | 没有创建、运行、消亡的状态变迁 |
### 进程
#### 定义
进程是程序的一次动态执行过程,是操作系统进行资源分配的基本单位。它就像按照菜谱进行烹饪的过程,需要占用资源,有始有终。它由以下三部分构成。
| 组成部分 | 作用 |
| :-------------------- | :----------------------------------------------------- |
| **程序段** | 描述进程要完成的功能,是控制进程执行的指令集 |
| **数据段** | 程序执行时所需的数据和工作区(全局变量、堆等) |
| **进程控制块(PCB)** | 包含进程的描述信息和控制信息,是**进程存在的唯一标志** |
#### 核心特征
- **动态性**:进程有生命周期(创建 → 就绪 → 运行 → 阻塞 → 终止)
- **独立性**:每个进程拥有独立的地址空间(虚拟内存空间、代码、数据、系统资源的集合)
- **并发性**:多个进程可在宏观上同时运行
- **资源拥有者**:进程拥有独立的文件描述符、环境变量、用户ID等资源
### 线程
#### 定义
线程是CPU进行运算调度的最小单位,是进程中的一条执行路径,一个进程至少有一个线程(也就是主线程)。它就像多位厨师共用厨房的资源(水、灶台、食材等),但是每人也都有自己的独有的刀具、围裙等,他们需要协调使用厨房做菜。
#### 核心特征
线程虽然共享进程的资源,但每个线程拥有自己的**私有数据**:
| **独有内容** | **说明** |
| :------------------ | :--------------------------------- |
| 线程 ID | 唯一标识 |
| 寄存器状态 | 包括程序计数器(PC)和栈指针 |
| 栈 | 独立运行栈,用于函数调用和局部变量 |
| 信号掩码 | 线程级别的信号处理设置 |
| 优先级 | 调度优先级 |
| 线程专用存储(TLS) | 线程私有的全局变量 |
## 用户态与内核态
### 内核态
运行操作系统内核代码,拥有对硬件的完全控制权,负责管理内存、进程、文件系统、网络等底层资源。
### 用户态
运行用户应用程序,无法直接访问硬件或内核数据结构,必须通过 系统调用(System Call)请求内核代为执行特权操作。
### 切换过程
为了保护系统安全与稳定,操作系统会对CPU的执行权限进行分级,不同的CPU架构有不同的等级,但是通常都遵循应用在最低特权级(用户态)运行,内核在次高特权级(内核态)运行,通过CPU硬件强制隔离特权操作。当在X86架构的CPU运行时,一个应用程序需要执行特权操作(比如读写文件),过程大致是这样的:
1. **触发**
程序调用 `read()` 等库函数,最终执行 CPU 的 `syscall` 指令。这条指令告诉 CPU:“我要切换到内核态干正事”。
2. **CPU 硬件自动切换**
CPU 在硬件层面做三件关键事:
- 把特权级从 **Ring 3(用户态)** 切换为 **Ring 0(内核态)**。
- 将栈指针从用户栈切换到**内核栈**(从 MSR 中读取预设的内核栈指针)。
- 把返回地址(下一条指令)和标志寄存器保存到特定寄存器(如 `rcx`、`r11`),然后跳转到内核预先设置好的**入口函数**。
3. **内核软处理**
入口函数将刚才没来得及保存的全部寄存器压入内核栈(形成 `pt_regs` 结构),然后根据**系统调用号**(放在 `rax` 通用寄存器中)从系统调用表里找到对应的内核函数(如 `ksys_read`)并执行。
4. **内核执行 & 返回**
内核函数完成实际工作(检查权限、读取数据等),把结果放在 `rax` 通用寄存器中。返回前,内核检查是否有其他任务需要处理(如信号、调度),然后通过 `sysret` 指令恢复之前保存的寄存器、用户栈和返回地址,特权级自动切回 **Ring 3**,程序从当初的下一条指令继续运行。
+45
View File
@@ -0,0 +1,45 @@
## Nginx 反代未透传客户端 IP 导致服务端日志全量变为 127.0.0.1
### 现象
后端解析IP后全量变成 `127.0.0.1`。
### 原因
后端解析IP的顺序是
```
X-Forwarded-For → X-Real-IP → RemoteAddr
```
**Nginx**转发至后端服务的 location 块缺少`proxy_set_header`指令:
```nginx
location /olive3 {
proxy_pass http://127.0.0.1:55430;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
```
### 修复
在缺失的 location 块中补充 header 透传:
```nginx
location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:55430;
# ... 其他配置不变
}
```
- `$proxy_add_x_forwarded_for`:将客户端真实 IP 追加到已有 `X-Forwarded-For` 尾部,而非覆盖
- `$remote_addr`:Nginx 直接看到的对端地址,作为 `X-Real-IP` 传递
+18
View File
@@ -0,0 +1,18 @@
## 快速复制文件路径
### Shift + 右键菜单(通用)
1. 在文件资源管理器中,**按住 `Shift` 键不放**
2. 右键点击目标文件
3. 选择 **「复制为路径(A)」**
### 功能区按钮
1. 选中文件
2. 点击资源管理器顶部的 **「主页」** 选项卡
3. 点击 **「复制路径」** 按钮(剪贴板图标)
### 拖拽到终端
- 直接把文件**拖入** PowerShell 或 CMD 窗口,会自动粘贴完整路径。
+123
View File
@@ -0,0 +1,123 @@
配置文件位置
```
C:\Users\admin\AppData\Roaming\Code\User\keybindings.json
```
该文件保存的是用户级快捷键列表,每一个快捷键配置都是包含`key`、`command`、`when`(可选)字段
| 字段 | 作用 |
| :------------ | :------------------------------- |
| **`key`** | 你按下的键盘组合 |
| **`command`** | 要执行的命令 ID |
| **`when`** | 规定这条快捷键在什么“语境”下生效 |
完整示例:
```json
// markdown 代码块
{
"key": "ctrl+shift+`",
"command": "markdown.extension.editing.toggleCodeBlock",
"when": "editorTextFocus && editorLangId == 'markdown'"
},
```
## 按键的书写规则
### 基本格式
- **全小写**:修饰键和字母键都要小写,写 `ctrl` 而不是 `Ctrl`,写 `a` 而不是 `A`。
- **用 `+` 连接**:按键之间用加号连接,不带空格。例如 `ctrl+shift+a`。
- **修饰键顺序任意**:`ctrl+alt+a` 和 `alt+ctrl+a` 效果相同,但习惯按 `ctrl -> shift -> alt -> 字母` 的顺序写
### 支持的修饰键
| 修饰键 | 写法 | 说明 |
| :------------ | :------ | :---------------------------------------------- |
| Ctrl | `ctrl` | Windows / Linux 上常用 `ctrl`,macOS 对应 `cmd` |
| Shift | `shift` | |
| Alt | `alt` | macOS 上是 `opt`(或者也支持 `alt`) |
| Cmd (Mac) | `cmd` | 在 Windows 上无效,建议用 `ctrl` 兼顾跨平台 |
| Win (Windows) | `win` | 很少用 |
| Meta (macOS) | `meta` | 通常等同 `cmd` |
### 特殊按键名称
| 按键 | 写法 |
| :----------- | :------------------------------------- |
| 方向键 | `up` `down` `left` `right` |
| 功能键 | `f1` ~ `f19` |
| 回车 | `enter` |
| 空格 | `space` |
| 退格 | `backspace` |
| Tab | `tab` |
| 反引号 | ``` (就是 backtick) |
| 逗号、句号 | `,` `.` 等直接写符号本身 |
| 数字键 | `0`~`9` |
| 计算器小键盘 | `numpad0` ~ `numpad9`,`numpad_add` 等 |
### 按键组合
VS Code 支持**二段按键**(一次按键序列),写法是:
```json
"key": "ctrl+k ctrl+b"
```
意思是先按 `Ctrl+K`,松开后再按 `Ctrl+B`。
## 命令ID
`command` 是一个**全局唯一的字符串**,由扩展或 VS Code 自己注册。不能自己编造,必须使用确切的 ID。
通过`ctrl+k ctrl+b`按键组合或者在设置中点击“键盘快捷方式”即可查看当前能设置的所有的命令。虽然显示的是命令,但是实际上是命令的名字,右键可以获取命令的ID。也可以不通过修改配置文件,在图形化界面设置快捷键。
## 上下文条件表达式
`when` 是一个**布尔表达式**,只有计算结果为 `true` 时,快捷键才会触发,可以组合变量使用逻辑运算符。
### 常用上下文变量
| 变量 | 说明 | 常见值 |
| :------------------------------ | :------------------------------------------------- | :------------------------------------------ |
| `editorTextFocus` | 焦点是否在编辑器的**文本区域**(光标可以输入文字) | `true` / `false` |
| `editorLangId` | 当前编辑器的语言 ID(不是文件后缀) | `"markdown"`, `"javascript"`, `"python"` 等 |
| `editorHasSelection` | 编辑器里是否有文本被选中 | `true` / `false` |
| `isLinux`, `isWindows`, `isMac` | 当前操作系统 | `true` / `false` |
| `resourceExtname` | 当前文件的后缀名(带`.`) | `".md"`, `".js"` 等 |
| `activeEditor` | 是否有激活的编辑器 | `true` / `false` |
| `terminalFocus` | 焦点是否在集成终端 | `true` / `false` |
| `listFocus` | 焦点在列表/树视图吗 | `true` / `false` |
### 逻辑运算符
| 运算符 | 含义 |
| :--------- | :----------------------------------------------------------- |
| `&&` | 与 |
| ` | |
| `!` | 非 |
| `==`, `!=` | 等于 / 不等于(字符串) |
| `=~` | 正则匹配(例如:`editorLangId =~ /^markdown$ |
| `in` | 测试左侧值是否在给定的右值数组里,如 `editorLangId in ['markdown', 'rmd']` |
### 例子分析
```json
"when": "editorTextFocus && editorLangId == 'markdown'"
```
表达式含义:确保你正在编辑文本,而不是在侧边栏、终端或其他地方**并且**当前文件被 VS Code 识别为 Markdown 语言
## 常见问题排查
| 现象 | 可能原因 |
| :------------------------------- | :------------------------------------------------------ |
| 按键完全没反应 | `command` 拼写错误;`when` 条件一直为 false;扩展未激活 |
| 在其他语言文件里也生效了 | 缺少 `editorLangId == 'xxx'` 限制,考虑加上 |
| 一次性按了没反应,但按键序列可以 | 可能你定义成了 chord,但习惯是单键 |
| 提示 “command 'xxx' not found” | 命令 ID 不存在,检查扩展是否已安装并启用 |
File diff suppressed because it is too large Load Diff
+225
View File
@@ -0,0 +1,225 @@
### **小六壬时课 · 知识库**
#### **一、 简介:什么是小六壬?**
小六壬,相传为三国时期诸葛亮、唐代李淳风所创,是一种简便、快速的预测方法,因其起课方便、便于携带(仅需掐指一算),故有“袖珍式预测学”或“马前课”之称。
其核心思想是,根据事发或问事时的“时间”或“心念”所触发的即时信息,来推演事物的状态和发展趋势。它主要用于对**单一、具体、短期**的事情进行快速占断,属于“一事一占”的动念占法。
**核心特点**:
- **简便快捷**:无需复杂工具,仅凭掌诀即可推算。
- **随机性强**:动念即可成课,捕捉瞬间天机。
- **应用广泛**:适用于寻物、问事、决疑、出行等日常生活的方方面面。
#### **二、 核心概念:六神宫位与掌诀**
小六壬共有六个基本“角色”,称为“六神”(或六宫),它们分布在食指、中指和无名指的指节上,形成一个固定的“掌上地盘”,即“掌诀”。
**掌诀位置图示**: 您可以伸出左手,按照下图在手指上定位六神:
- **大安**:食指根部
- **留连**:食指尖部
- **速喜**:中指尖部
- **赤口**:无名指尖部
- **小吉**:无名指根部
- **空亡**:中指根部
(其顺序为顺时针循环)
**六神基础信息表**:
| 宫位/六神 | 五行 | 吉凶 | 核心意象与关键词 |
| :------------------ | :--- | :--- | :--------------------------------- |
| **大安 (Dà'ān)** | 木 | 吉 | 安泰、平稳、静止、官方、身体、龙 |
| **留连 (Liúlián)** | 水 | 凶 | 延迟、纠缠、牵绊、缓慢、暧昧、暗昧 |
| **速喜 (Sùxǐ)** | 火 | 吉 | 快速、喜讯、文书、宴饮、光明、朱雀 |
| **赤口 (Chìkǒu)** | 金 | 凶 | 口舌、官非、争斗、惊恐、凶险、白虎 |
| **小吉 (Xiǎojí)** | 木 | 吉 | 合作、财利、和合、顺畅、六合 |
| **空亡 (Kōngwáng)** | 土 | 凶 | 落空、无果、忧虑、阴私、迷茫、玄武 |
#### **三、 主要起课方法**
起课,就是根据参数确定最终落在哪一个宫位上。
**1. 传统时间起课法(月、日、时)** 这是最正统的方法,以农历时间为准。
- 第一步:月上起日
- 从**大安**宫开始,顺时针将月份数代入。例如,一月为大安,二月为留连,三月为速喜……以此类推。
- 第二步:日上起时
- 从上一步月份的落宫开始,顺时针将日期数(初一、初二……)代入。
- 第三步:时上定宫
- 从上一步日期的落宫开始,顺时针将时辰数(子时为1,丑时为2……)代入,最终落下的宫位即为结果。
**【示例】**:占测农历八月初十下午4点(申时)。
1. **定月**:从大安宫数8个月,落点在**赤口**。(1大安→2留连→3速喜→4赤口→5小吉→6空亡→7大安→8赤口)
2. **定日**:从赤口宫数10日,落点在**小吉**。(1小吉→2空亡→...→6赤口→...→10小吉)
3. 定时
:申时是第9个地支。从上一步的小吉宫数9个时辰,最终落点在
速喜
。
- **结果**:速喜。
**2. 报数起课法** 当不知晓具体时辰或为求心念感应时使用。
- 让问事者随意报出**三个数字**(1位数到3位数均可)。
- 将每个数字分别**除以6,取其余数**(若余数为0,则按6计算)。
- **第一步**:从“大安”宫起,按第一个余数顺时针数,定下基点。
- **第二步**:从基点宫位起,按第二个余数顺时针数,定下过程点。
- **第三步**:从过程点起,按第三个余数顺时针数,定下最终结果。
**【示例】**:报数为 7, 11, 32。
1. **第一数**:`7 ÷ 6 = 1 ... 余 1`。从大安数1,落在**大安**。
2. **第二数**:`11 ÷ 6 = 1 ... 余 5`。从大安数5,落在**小吉**。
3. 第三数
:
```
32 ÷ 6 = 5 ... 余 2
```
。从小吉数2,落在
空亡
。
- **结果**:空亡。
#### **四、 六神象义详解与分类占断**
这是解卦的核心,需要结合所问之事灵活判断。
**1. 大安**
- **核心**:身不动,心安宁。静待为吉,妄动有失。
- **问事**:事可成,但需时日,不可急躁。过程平顺。
- **寻物/寻人**:人或物未走远,在家中或原地,或在东方、东北方,多在静态、稳定的环境中(如办公室、卧室)。
- **感情**:关系稳定,感情安泰,但可能缺乏激情。
- **财运**:财运稳定,宜守成,不宜投机。
- **健康**:身体安康,即使有病也易于恢复。
**2. 留连**
- **核心**:事未了,心纠结。拖延、纠缠、暧昧不清。
- **问事**:事情进展缓慢,有阻碍,难以立刻解决,容易节外生枝。
- **寻物/寻人**:难寻,或被某事/某人牵绊住。物品可能在潮湿、脏乱或纠缠之地。
- **感情**:关系纠缠不清,或处于冷战、延迟状态。有藕断丝连之象。
- **财运**:财运不畅,资金周转困难,或有债务纠纷。
- **健康**:病情迁延,难以速愈,多为慢性病或旧病复发。
**3. 速喜**
- **核心**:火光之象,喜信速至。宜速战速决,不宜拖延。
- **问事**:事能速成,有喜讯或好结果。尤其利于文书、考试、信息类事宜。
- **寻物/寻人**:能快速找到。人或物在南方,或在热闹、明亮之处。
- **感情**:进展神速,热情洋溢,或有喜事临门(如订婚、结婚)。
- **财运**:偏财运旺,有意外之财,或短期投资获利。
- **健康**:多为急症、热症(如发烧、炎症),但来得快去得也快。
**4. 赤口**
- **核心**:白虎开口,是非官讼。主凶险、争斗、惊扰。
- **问事**:事多纷争,易有口舌是非,甚至官司诉讼。过程不顺,需防小人。
- **寻物/寻人**:难寻。多有争吵或处于危险境地。物品可能已损坏。
- **感情**:关系紧张,争吵频繁,易有分离之象。
- **财运**:破财之象,谨防因纠纷、官非而损财。
- **健康**:需防意外伤害、手术、或急性恶疾。有血光之灾的警示。
**5. 小吉**
- **核心**:六合之象,诸事和合。代表合作、财利、转机。
- **问事**:事情顺利,有贵人相助,适宜合作、商议。
- **寻物/寻人**:易寻。人或物在附近,或与友人、同伴在一起。
- **感情**:关系和谐,琴瑟和鸣,是发展的良好时机。
- **财运**:正财、合伙之财运佳,交易顺利。
- **健康**:病情好转,遇到良医,康复有望。
**6. 空亡**
- **核心**:万事成空,虚无之象。信息不实,劳而无功。
- **问事**:事情没有头绪,心中没底,最终可能不了了之或毫无结果。有虚假、欺骗的成分。
- **寻物/寻人**:人已远去,物已丢失,找回希望渺茫。
- **感情**:关系空虚,前景不明,或对方心意不诚。
- **财运**:财运空空,投资无望,谨防被骗。
- **健康**:病情诊断不明,或查无此症,也可能指精神、心理层面的问题。
#### **五、 断辞内容**
1、大安:身不动时,属木,为青龙,凡谋事主一、五、七。
断辞曰:
大安事事昌,求谋在东方,失物去不远,
宅舍保安康,行人身未动,病者主无妨,
将军回田野,仔细与推详。
2、留连:卒未归时,属水,为玄武,凡谋事主二、八、十。
断辞曰:
留连事难成,求谋曰未明,官事只宜缓,
去者未回程,失物南方见,急讨方称心,
更须防口舌,人口且平平。
3、速喜:人便至时,属火,为朱雀,凡谋事主三、六、九。
断辞曰:
速喜喜来临,求财向南行,失物申午未,
逢人路上寻,官事有福德,病者无祸侵,
田家六畜吉,行人有信音。
4、赤口:官事凶时,属金,为白虎,凡谋事主四、七、十。
断辞曰:
赤口主口舌,官非切要防,失物急去寻,
行人有惊慌,鸡犬多作怪,病者出西方,
更须防咒诅,恐怕染蕴疾。
5、小吉:人来喜时,属木,为六合,凡谋事主一、五、七。
断辞曰:
小吉最吉昌,路上好商良,阳人来报喜,
失物在坤方,行人立便至,交关真是强,
凡事皆和合,病者事无仿。
6、空亡:音信稀时,属土,为勾陈,凡谋事主一、五、七。
断辞曰:
空亡事不长,阴人小乘张,求财无有利,
行人有灾殃,失物寻不见,官事主刑伤,
病人逢暗鬼,乞解保安康。
#### **六、 使用原则与心法**
- **心诚意专**:占测时心态必须虔诚、专注,意念集中于所问之事。
- **一事一占**:同一件事在短期内不应反复占问,以第一次的结果为准。所谓“再三渎,渎则不告”。
- **动念即占**:当某个问题或念头强烈地出现在脑海中时,是最佳的占测时机。
- **趋吉避凶**:小六壬提供的是一种预警和参考。得到吉卦,应顺势而为;得到凶卦,则应反思、谨慎、规避,而非坐以待毙。它是一种积极的“行为指导”,而非消极的“宿命论”。
+21
View File
@@ -0,0 +1,21 @@
## CONCAT函数
`CONCAT` 是 SQL 中用于**字符串拼接**的函数,它可以将两个或多个字符串合并成一个新的字符串。
------
### 基本语法
```sql
CONCAT(string1, string2, ..., stringN)
```
### 不同数据库的 CONCAT 行为
| 数据库 | 行为 |
| :--------------------- | :----------------------------------------------- |
| **MySQL** | 支持多参数;**任一参数为 NULL,整个结果为 NULL** |
| **SQL Server** (2012+) | 支持多参数;**NULL 会被自动转为空字符串** `''` |
| **Oracle** | 只支持 **2 个参数**;NULL 视为空字符串 |
| **PostgreSQL** | 支持多参数;NULL 视为空字符串 |
+44
View File
@@ -0,0 +1,44 @@
## B-Tree
**适用场景**
| 场景 | 示例 |
| :------- | :-------------------------- |
| 等值查询 | WHERE user_id = 100 |
| 范围查询 | WHERE age BETWEEN 20 AND 30 |
| 排序优化 | ORDER BY created_at DESC |
| 前缀匹配 | WHERE name LIKE '张%' |
**不适用场景**
- LIKE '%关键词%'(后缀/中间模糊匹配)
- 全文搜索
- 数组包含查询
## GIN
**结构特点**
- 倒排索引(Inverted Index) 机制:从"内容"映射到"行"
- 索引的是复合值内部的独立元素
- 索引构建较慢,但查询性能极佳
**适用场景**
> `@>`表示包含、`<@`表示被包含、`&&`表示重叠、`@@`表示全文搜索匹配、`?`表示JSONB 键存在
| 场景 | 示例 |
| :----------- | :----------------------------------------------------- |
| 全文搜索 | WHERE to_tsvector(content) @@ to_tsquery('postgresql') |
| 数组包含 | WHERE tags @> ARRAY['java', 'sql'] |
| JSONB 查询 | WHERE data @> '{"status": "active"}' |
| 多值元素搜索 | WHERE arr && ARRAY[1, 2] |
+60
View File
@@ -0,0 +1,60 @@
## 权重抽奖
### 轮盘赌
适合奖品数量较少,一次性抽奖的场景
核心顺序遍历,逐个减权重:是用一个随机数 `r`,依次减去每个奖品的权重,当 `r` 变成负数的那一刻,说明它“落入了”当前奖品的权重区间。时间复杂度 **O(n)**,空间复杂度**O(1)**
```go
func Draw(rewards []*Reward) *Reward {
totalWeight := 0
for _, r := range rewards {
totalWeight += r.Weight
}
r := rand.Intn(totalWeight) // [0, totalWeight)
for _, reward := range rewards {
r -= reward.Weight
if r < 0 {
return reward
}
}
return rewards[len(rewards)-1] // 兜底,理论上不会走到
}
```
### 前缀和 + 二分查找
适合奖品数量较多,需要频繁抽奖的通用场景
核心是构建前缀和数组,二分查找:把权重想象成一条线段,每个奖品占据一段,生成 `[0, 100)` 的随机数,落在哪个区间就中哪个奖品。二分查找快速定位区间,时间复杂度 **O(log n)**,空间复杂度**O(n)**
```
权重: 50 25 15 7 2 1
├─────────┼────────┼──────┼────┼──┼──┤
0 50 75 90 97 99 100
← 谢谢参与 →←5元券→←10元券→←月卡→←年卡→←大奖→
```
```go
type Reward struct {
Name string
Weight int
}
func Draw(rewards []*Reward) *Reward {
prefixSumArray := make([]int, len(rewards)) // 前缀和数组
totalWeight := 0 // 总权重
for i, p := range rewards {
totalWeight += p.Weight
prefixSumArray[i] = totalWeight
fmt.Println(totalWeight)
}
r := rand.Intn(totalWeight) // r ∈ [0, totalWeight-1]
idx := sort.SearchInts(prefixSumArray, r+1) // 查找目标 ∈ [1, totalWeight]
return rewards[idx]
}
```
+56
View File
@@ -0,0 +1,56 @@
## 轮询算法
### 平滑加权轮询
#### 原理
每台服务器维护一个 `当前权重`(currentWeight),每次选择 `currentWeight` 最大的服务器,选中后将其 `currentWeight` 减去总权重,然后**所有服务器都加上自己的固定权重**。如此循环,高权重节点被选中的频率更高,但不会连续出现。
#### 实际应用
- **Nginx** 的 `upstream` 默认负载均衡算法
- **LVS**(Linux Virtual Server)
- 几乎所有需要「按比例分配且平滑」的场景
#### 轮询示例
```go
type server struct {
name string
weight int
curWeight int
}
func NextServer(servers []*server) *server {
var totalWeight int
// 1.先给所有服务器加上各自的固定权重
for i := range servers {
servers[i].curWeight += servers[i].weight
totalWeight += servers[i].weight
}
// 2.再找出 curWeight 最大的服务器
maxIdx := 0
for i := 1; i < len(servers); i++ {
if servers[i].curWeight > servers[maxIdx].curWeight {
maxIdx = i
}
}
// 3.选中的服务器减去总权重
servers[maxIdx].curWeight -= totalWeight
return servers[maxIdx]
}
```
| 轮次 | 操作前 (currentWeight) | 选中 | 操作后 |
| ---- | ---------------------- | ----- | --------------- |
| 初始 | A=0, B=0, C=0 | — | — |
| 1 | A=5, B=1, C=1 | **A** | A=−2, B=1, C=1 |
| 2 | A=3, B=2, C=2 | **A** | A=−4, B=2, C=2 |
| 3 | A=1, B=3, C=3 | **B** | A=1, B=−4, C=3 |
| 4 | A=6, B=−3, C=4 | **A** | A=−1, B=−3, C=4 |
| 5 | A=4, B=−2, C=5 | **C** | A=4, B=−2, C=−2 |
| 6 | A=9, B=−1, C=−1 | **A** | A=2, B=−1, C=−1 |
| 7 | A=7, B=0, C=0 | **A** | A=0, B=0, C=0 |
+123
View File
@@ -0,0 +1,123 @@
## API
### goctl代码生成
可以在doc/genCode.md查看
```powershell
# 生成多个types类型的话需要关闭这个实验性功能
goctl env -w GOCTL_EXPERIMENTAL=off
# 代码生成,会自动处理引入的子api文件
goctl api go -api background.api -dir . --home D:/zhangyuxin/template/1.8.3
goctl api go -api login.api -dir . --home ../../../deploy/goctl
goctl api go -api main.api -dir . --home ../../deploy/goctl
```
### 参数校验
| 接收规则 | 说明 | 示例 |
| -------- | ------------------------------------------ | ------------------------------- |
| optional | 当前字段是可选参数,允许为零值(zero value) | `json:"foo,optional"` |
| options | 当前参数仅可接收的枚举值 | `json:"gender,options=foo|bar"` |
| default | 当前参数默认值 | `json:"gender,default=male"` |
| range | 当前参数数值有效范围,支持开/闭区间 | `json:"age,range=[0:120]"` |
#### **`optional`** **与** **`omitempty`** **的区别**
两者都写在 json/form tag 的 options 里,但作用域完全不同,不可互相替代
##### `optional` —— goctl 专用,控制 Swagger 与请求校验
- **Swagger 生成**:标了 `optional` 的字段不进 `required` 数组,Apifox/Swagger UI 显示为可选;未标的字段进 `required` 数组,显示为必填
- **请求校验**:goctl 生成的 logic 不会对标了 `optional` 的字段做必填校验
- **判定逻辑**:goctl 遍历 tag options,找到 `optional` 关键字即判定为可选
##### `omitempty` —— Go 标准库指令,控制 JSON 序列化
- **JSON 序列化**:字段为零值(0、""、nil、false)时,响应 JSON 中**不输出**该字段
- **Swagger 生成**:**goctl 完全不识别**,字段仍按未标 `optional` 处理,即判定为 required
- **请求校验**:无影响
## RPC
### goctl代码生成
```powershell
# 主proto文件代码生成
goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. --proto_path=. -m --home ../../../deploy/goctl
# 主proto文件中引入的proto代码生成
protoc.exe -I ./ --go_out=. --go-grpc_out=. ./user_model.proto
```
当`proto`文件里面定义了多个 service的时候,需要额外注意,还要再main函数中手动注册一下
> 每个 proto 文件应该聚焦于一个核心概念/实体。一个文件里定义太多 service 通常是设计需要重新考虑的信号
注册示例
```go
import (
ebookServer "olive/services/product/rpc/internal/server/ebookservice"
)
var configFile = flag.String("f", "etc/product.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
// 新服务
productrpcmodel.RegisterEbookServiceServer(grpcServer, ebookServer.NewEbookServiceServer(ctx))
})
defer s.Stop()
fmt.Printf("Starting product RPC server at %s...\n", c.ListenOn)
s.Start()
}
```
### proto代码生成错误:File not found错误
- 错误描述
执行 protoc 报 google/protobuf/timestamp.proto: File not found. 的错提示File not found
- 解决方式
重新下载[protoc](https://github.com/protocolbuffers/protobuf/releases),解压后,将整个include目录复制到protoc执行文件同一级目录下
## model
可以在doc/genCode.md查看
**Model 层**:只做 **“业务循环”** + **“SQL 查询”** + **“结果封装”**,不关心字符串到时间的转换和 Protobuf 类型。
### goctl代码生成
```powershell
# mysql根据远程数据库表生成
goctl model mysql datasource --url="olive:xxvkikaDDx01&&99@tcp(127.0.0.1:3306)/ebming" --table="user_invitation_record" --dir="." -c --home ../../../../deploy/goctl/1.8.3
# mysql根据ddl生成
goctl model mysql ddl --src h5.sql --dir . -c --home D:/zhangyuxin/template/1.8.3
# pgsql根据远程数据库表生成
goctl model pg datasource -url "postgres://labman:tN5bWz2KpL9Fy4@127.0.0.1:15432/sern" -table user -dir ./user --cache --home ../../goctl_template/1.8.3
# pgsql根据远程数据库表生成,带模式(schema)
goctl model pg datasource -url "postgres://admin:zhangyuxin@127.0.0.1:25432/test?sslmode=disable" -s "users" -table "profile" -dir ./go-zero/model/users/ --cache --home ../../deploy/goctl
```
+263
View File
@@ -0,0 +1,263 @@
## 正确关闭`HTTP`响应
> **注意事项**
>
> - 先检查 `err`,再 `defer close`
>
> - 必须消费完响应体,否则连接无法复用
>
> - `defer` 会延迟到函数结束,如果需要在循环中处理大量响应,建议手动关闭(避免 `defer` 累积)
```go
resp, err := http.Get("https://example.com")
if err != nil {
// 处理请求错误(此时 resp 可能为 nil)
return err
}
// 确保在函数退出时关闭响应体
defer resp.Body.Close()
// 手动关闭,不用 defer
// io.Copy(io.Discard, resp.Body)
// resp.Body.Close()
// 读取或处理响应体(即使不需要内容也建议消费完)
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
// 使用 body ...
```
## 切片内存泄漏
> 即使原切片变量本身被置为 `nil` 或超出作用域,只要**还存在任何一个指向其底层数组的子切片**,整个底层数组就不会被回收
如果新切片需要长期保存,而原切片很大且即将被丢弃,那么**直接使用原切片[:n] 会导致新切片底层仍然引用原切片的整个底层数组**,使得那个大数组无法被 GC 回收,造成内存泄漏。此时应该用 `copy` 创建一份独立的、只包含所需元素的新切片。
```go
func getFirstLines() []string {
hugeLines := readHugeFile() // 假设这个切片有 100 万行,占用 100MB 内存
// 错误做法:返回子切片
return hugeLines[:3] // 虽然只返回3行,但底层数组的 100MB 内存无法被回收!
}
// 正确做法
func getFirstLines() []string {
hugeLines := readHugeFile()
// 只拷贝需要的3行
result := make([]string, 3)
copy(result, hugeLines[:3])
return result // 原来的 hugeLines 可以被GC回收,只占用很小的内存
}
```
## 堆栈
### 核心区别
| 特性 | 栈 | 堆 |
| --------- | -------------- | ------------------ |
| 分配/释放 | 移动指针,极快 | 需要查找 + GC 回收 |
| 生命周期 | 函数返回即销毁 | GC 决定何时回收 |
| 内存布局 | 连续,无碎片 | 分散,有碎片 |
| 大小限制 | 较小(MB级) | 较大(GB级) |
### 逃逸判断规则
```
变量在以下情况逃逸到堆:
1. 返回指针 → func f() *int { x := 42; return &x }
2. 存入全局变量 → globalPtr = &x
3. 闭包捕获 → return func() { return x }
4. 发送到 channel → ch <- &x
5. 存入外部结构体 → c.Value = &x
6. 接口装箱逃逸 → return interface{}(x)
7. 反射调用 → fmt.Println(x)
8. 大对象 → make([]int, 10000)
```
### 常见误区澄清
| 误区 | 正解 |
| ------------------ | -------------------------------- |
| 指针一定在堆上 | ❌ 函数内创建和使用的指针可在栈上 |
| 接口一定在堆上 | ❌ 不逃逸的接口内部值可在栈上 |
| 结构体指针一定逃逸 | ❌ 小结构体 + 内联优化可能栈分配 |
### 验证方法
```bash
# 查看逃逸分析
go build -gcflags="-m" main.go
# 更详细信息
go build -gcflags="-m -m" main.go
```
### 优化建议
```go
// ✅ 推荐:小对象返回值
func getUser() User {
return User{
ID: 1
}
}
// ❌ 避免:小对象返回指针(增加 GC 压力)
func getUser() *User {
return &User{
ID: 1
}
}
// ✅ 推荐:传指针给只读函数
func read(p *Data) int {
return p.Value
}
// ❌ 避免:在热路径使用 fmt.Print
// 用 strconv 或 log 代替
```
### 记忆口诀
```
生命周期越界 → 堆
外部有引用 → 堆
闭包捕获 → 堆
反射调用 → 堆
函数内使用 → 栈
返回值拷贝 → 栈
只读指针 → 栈
```
### 本质理解
**栈** = 函数的私有工作区,函数结束自动清理
**堆** = 共享仓库,需要 GC 定期打扫
逃逸分析 = 编译器判断"这个变量能不能安全地放在栈上"
## 类型转换/序列化
```
//int64转换string
strconv.FormatInt(num,10)
//序列化为string
jsoniter.ConfigCompatibleWithStandardLibrary.MarshalToString(input)
```
在 encode/decode JSON 数据并且数据类型是interface时,Go 默认会将数值当做 float64 处理
```go
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Height float64 `json:"height"`
}
type UserInter struct {
Name interface{} `json:"name"`
Age interface{} `json:"age"`
Height interface{} `json:"height"`
}
func TestJson(t *testing.T) {
one := &User{
Name: "tom",
Age: 18,
}
oneJson, err := jsoniter.Marshal(one)
if err != nil {
fmt.Println(err)
return
}
var two UserInter
var three map[string]interface{}
err = jsoniter.Unmarshal(oneJson, &two)
err = jsoniter.Unmarshal(oneJson, &three)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(reflect.TypeOf(two.Name))
fmt.Println(reflect.TypeOf(two.Age)) //会打印float64
fmt.Println(reflect.TypeOf(two.Height)) //会打印float64
fmt.Println(reflect.TypeOf(three["name"]))
fmt.Println(reflect.TypeOf(three["age"])) //会打印float64
fmt.Println(reflect.TypeOf(three["height"])) //会打印float64
}
```
## `HTTP`阅后即焚
处理 HTTP Body 只能读一次的问题时,根据你的**数据量大小**选择武器:
1. **小请求(配置、JSON)**:使用 `io.ReadAll` 存入内存,再通过 `io.NopCloser` 塞回 `r.Body` 或设置 `r.GetBody`。
2. **中等请求(签名校验)**:使用 `io.TeeReader` 边读边处理,节省一次全量扫描。
3. **大请求(文件上传)**:**禁止 ReadAll**。直接流式 `io.Copy` 到目的地,或使用临时文件做中转。
## map的坑
go的map内部是哈希表,元素会因 rehash(扩容)而移动到新的内存位置,如果允许 `&m[key]` 拿到一个指针,map 一旦扩容,这个指针就变成悬空指针(指向已失效的内存),为了避免这种内存安全问题,Go 直接禁止对 map 值取地址,下面是示例代码:
```go
type User struct {
Name string
Age int
}
func main() {
m := map[int]User{1: {Name: "张三", Age: 20}}
// ❌ 编译错误:cannot assign to struct field m[1].Name in map
m[1].Name = "李四"
// ✅ 正确的替代写法
temp:=m[1]
temp.Name = "李四"
m[1]=temp
}
```
> **可寻址(addressable)**:能通过 `&` 拿到它内存地址的操作数。
>
> **不可寻址(not addressable)**:不能通过 `&` 拿到地址的操作数。
## 句点引入
一个完整引入声明语句形式的引入名`importname`可以是一个句点(`.`)。 这样的引入称为句点引入。使用被句点引入的包中的导出代码要素时,限定标识符的前缀必须省略。
比如在下面这个例子中,`Println`和`Now`函数调用不需要带任何前缀。
```go
package main
import (
. "fmt"
. "time"
)
func main() {
Println("Current time:", Now())
}
```
> 一般来说,句点引入不推荐使用,因为它们会导致较低的代码可读性
+55
View File
@@ -0,0 +1,55 @@
## 交叉编译
### 基本用法
```bash
# Linux/macOS
GOOS=目标系统 GOARCH=目标架构 go build -o 输出文件名 源文件
# Windows (PowerShell)
$env:GOOS="目标系统"; $env:GOARCH="目标架构"; go build -o 输出文件名 源文件
# Windows (CMD)
set GOOS=目标系统 && set GOARCH=目标架构 && go build -o 输出文件名 源文件
```
查看所有支持的平台
```bash
go tool dist list
```
### 常用组合
| 目标平台 | GOOS | GOARCH |
| ------------ | ------- | ------ |
| Linux 64位 | linux | amd64 |
| Linux ARM64 | linux | arm64 |
| Windows 64位 | windows | amd64 |
| macOS Intel | darwin | amd64 |
| macOS M1/M2 | darwin | arm64 |
### 示例
```bash
# Linux/Mac (Bash)
# 编译 Linux 64位
GOOS=linux GOARCH=amd64 go build -o app ./app/api/main.go
# 编译 Windows 64位
GOOS=windows GOARCH=amd64 go build -o app.exe ./app/api/main.go
# 编译 macOS ARM64 (M1/M2)
GOOS=darwin GOARCH=arm64 go build -o app ./app/api/main.go
# Windows (PowerShell)
# 编译 Linux 64位
$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o app ./app/api/main.go
# 编译 Windows 64位
$env:GOOS="windows"; $env:GOARCH="amd64"; go build -o app.exe ./app/api/main.go
# 编译 macOS ARM64 (M1/M2)
$env:GOOS="darwin"; $env:GOARCH="arm64"; go build -o app ./app/api/main.go
```
@@ -0,0 +1,117 @@
### validator
[GitHub地址](github.com/go-playground/validator)
包验证器,可以用于验证参数是否符合规则,比如下方的比较验证,还有其他可以查看网址的详细介绍
go-zero自带的有校验器,可以使用
| Tag | Description |
| ---- | --------------------- |
| eq | Equals |
| gt | Greater than |
| gte | Greater than or equal |
| lt | Less Than |
| lte | Less Than or Equal |
| ne | Not Equal |
### goverter
[GitHub地址](https://github.com/jmattheis/goverter)
类型转换器,可以用于类型的转换,比如下面的代码
```go
func (l *CreatePopupLogic) CreatePopup(in *backgroundrpcmodel.ResPopup) (*backgroundrpcmodel.ResResult, error) {
_, err := l.svcCtx.PopupModel.Insert(l.ctx, u.Convert.SysPopupR2S(in.Info))
if err != nil {
return nil, rerr.NewFromSQL(err)
}
return &backgroundrpcmodel.ResResult{
Result: true,
}, nil
}
```
使用
```
goverter gen .
```
### asynq
[GitHub网址](https://github.com/hibiken/asynq)
- 入队流程
```go
payload := &jobtype.xxxPayload{}
payloadByte, err := jsoniter.Marshal(payload)
if err != nil {
logt.Error("xxx jsoniter.Marshal err:", err)
return nil, zerr.New(rescode.DataFormatConversionError)
}
_, err = l.svcCtx.AsynqClient.Enqueue(asynq.NewTask(jobtype.xxx, payloadBytes, asynq.Queue(consts.QueueLow)))
if err != nil {
logt.Error("xxx enqueue err:", err)
return nil, zerr.New(rescode.EnqueueErr)
}
```
- 自行定义了队列优先级,在入队的时候可以加参数进行选择
```
consts.QueueCritical: 6,
consts.QueueDefault: 3,
consts.QueueLow: 1,
```
- 处理前流程(反序列化,获取数据)
```go
var payload jobtype.xxx
err := json.Unmarshal(task.Payload(), &payload)
if err != nil {
logt.Error("xxx unmarshal payload err:", err)
return zerr.New(rescode.DataFormatConversionError)
}
```
### gorm
[官网文档](https://gorm.io/zh_CN/docs/)
**使用gorm + sqlx,但是复用sql连接**
```go
func NewServiceContext(c config.Config) *ServiceContext {
logx.Infof("Cache config: %+v", c.CacheRedis)
gormDB, err := gorm.Open(postgres.Open(c.Database.DataSource), &gorm.Config{})
if err != nil {
msg := fmt.Sprintf("连接数据库失败 %s ", err.Error())
logx.Error(msg)
panic(msg)
}
sqlDB, err := gormDB.DB()
if err != nil {
msg := fmt.Sprintf("获取底层连接失败: %s", err.Error())
logx.Error(msg)
panic(msg)
}
sqlxConn := sqlx.NewSqlConnFromDB(sqlDB)
return &ServiceContext{
Config: c,
UserAuthModel: models.NewUserAuthModel(sqlxConn, c.CacheRedis, gormDB),
}
}
```
+73
View File
@@ -0,0 +1,73 @@
Go 1.24 起,`map` 的底层实现从传统的 `hmap` + `bmap` 哈希表,彻底切换到了 Swiss Table(瑞士表)
![Gemini_Generated_Image_jhqwi7jhqwi7jhqw](D:\zhangyuxin\code\lab\note\assets\golang-map[swiss table].png)
### Group内存布局图
你可以直接复制以下文本块到你的 Markdown 笔记中(使用 text 包裹):
codeText
```
+-------------------------------------------------------------------------------+
| 单 Group 内存布局 |
+--------------------------+----------------------------+-----------------------+
| 1. Metadata 控制字节区 | 2. 连续 Key 存储区 | 3. 连续 Value 存储区 |
+--------------------------+----------------------------+-----------------------+
| [M0][M1][M2]...[M7] | [K0][K1][K2]...[K7] | [V0][V1][V2]...[V7] |
+--------------------------+----------------------------+-----------------------+
| <- 固定 8 个字节 -> | <- 长度取决于 Key 类型 -> | <- 长度取决于 Val -> |
| <- 一次性载入寄存器对比 -> | |
```
**为什么这样设计?(设计优势):**
1.
2. **避免内存对齐填充(Padding)**:如果采用 [Meta][Key][Value] 交叉排列,由于不同类型的 Key/Value 字节大小不同,为了对齐,Go 必须在它们之间插入大量空白填充字节,浪费空间。分块连续存储消除了组内的对齐损耗。
3. **对 CPU 缓存友好**:8 字节的 Metadata 紧密相连,一次 Cache Line 填充就能把整组的控制信息全部读入,极大地提高了缓存命中率。
------
### 读取流程
以下为您整理的完整、正确的读取逻辑,您可以直接复制使用:
#### 1.哈希计算与分段 (Hash Splitting)
- 将 Key 传入哈希函数,计算出一个 **64 位的哈希值**。
- 该哈希值被拆分为两部分:**H1(高 57 位)**:用于定位哈希表中的初始 Group 索引。**H2(低 7 位)**:作为特征值(指纹),存储在 Group 的 Metadata 中,用于快速匹配。
#### 2.定位初始 Group (Locate Group)
- 计算初始 Group 索引:group_index = H1 & (Group数量 - 1)。
> 可以理解与$2^B$进行取模运算,但是**位与运算**效率更高且等价。
#### 3.组内快速匹配 (Group-level SWAR Match)
- **一次性加载**:将目标 Group 的 8 字节 Metadata 一次性载入一个 64 位寄存器中。
- **并行对比(SWAR 技术)**:利用位运算,将 7 位的 H2 同时与这 8 个 Metadata 字节进行并行对比,输出一个 8 位的位掩码(Bitmask)。位掩码中为 1 的位,代表该位置的 H2 与目标 H2 匹配成功。这一步极快,完全避免了循环遍历。
#### 4.冲突解决与探测循环 (Verification & Probing Loop)
根据位掩码的匹配结果,执行以下逻辑:
1. 依次根据匹配成功的索引,去 **Key 存储区** 找到对应的真实 Key。
2. 进行完整的 Key 等值对比(如 if key == targetKey):**若匹配成功**:直接去 **Value 存储区** 对应偏移量取出 Value 并返回。**若匹配失败**:说明发生了 **H2 哈希冲突**(即不同的 Key 产生了相同的 H2),继续对比下一个匹配的槽位。
3. 如果当前 Group 中所有匹配了 H2 的槽位都对比完了,仍未找到匹配的 Key,则**跳转至【情况 B】**。
此时需要检查当前 Group 的 Metadata 状态,决定是继续向后寻找还是停止:
- **若当前 Group 中存在“空位”(Empty,0xFF)**:
说明探测链在此处断开,该 Key 在 Map 中**一定不存在**,直接返回 nil 或零值。
- **若当前 Group 中无空位,但存在“墓碑”(Tombstone,0x80)或已满**:
虽然当前 Group 没找到,但可能因为历史删除操作(留下了墓碑)或哈希冲突,导致数据被存到了后续的 Group。不能停止!使用**二次探测算法(Quadratic Probing)**计算下一个 Group 的索引,跳转回 **【3.组内快速匹配】** 继续循环寻找。
@@ -0,0 +1,17 @@
## 命名规则
> [《布尔变量起名的艺术》](https://thatamazingprogrammer.com/posts/stop-naming-your-variables-flag-the-art-of-boolean-prefixes/)
### 使用正确的前缀
(1)**is**:描述事物的状态,后面跟形容词,比如 isActive,isDeleted,isEmpty。
(2)**has**:描述事物的所有权或包含关系,后面跟名词,比如 hasAccess,hasChildren,hasValidationErrors。
(3)**can**:描述事物的能力或权限,比如 canEdit,canDelete,canRetry。
(4)**should**:描述事物的意图或逻辑,比如 shouldRetry,shouldCacheResponse。
### 其他
永远不在布尔变量名中使用否定词,比如isDisabled,因为当使用在if语句中会让大脑进行双重否定运算
+120
View File
@@ -0,0 +1,120 @@
### URL设计规范
URL为统一资源定位器 ,接口属于服务端资源,通常一个完整的URL组成由以下几个部分构成:
```
URI = scheme "://" host ":" port "/" path [ "?" query ][ "#" fragment ]
```
- **scheme**: 指底层用的协议,如http、https、ftp
- **host**: 服务器的IP地址或者域名
- **port**: 端口,http默认为80端口
- **path**: 访问资源的路径,就是各种web 框架中定义的route路由
- **query**: 查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
- **fragment**: 锚点,定位到页面的资源
**通常一个RESTful API的path组成**如下:
```undefined
/{version}/{resources}/{resource_id}
```
- **version**:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。
- **resources**:资源,RESTful API推荐用小写英文单词的复数形式。
- **resource_id**:资源的id,访问或操作该资源。
有时候可能资源级别较大,其下还可细分很多子资源也可以灵活设计URL的path,例如:
```undefined
/{version}/{resources}/{resource_id}/{subresources}/{subresource_id}
```
当增删改查无法满足业务要求,可以在URL末尾加上action,例如
```
/{version}/{resources}/{resource_id}/action
```
其中action就是对资源的操作。
**RESTful API的URL具体设计的规范**如下:
1. 不用大写字母,所有单词使用英文且小写。
2. 连字符用中杠`"-"`而不用下杠`"_"`
3. 正确使用 `"/"`表示层级关系,URL的层级不要过深,并且越靠前的层级应该相对越稳定
4. 结尾不要包含正斜杠分隔符`"/"`
5. URL中不出现动词,用请求方式表示动作
6. 资源表示用复数不要用单数
7. 不要使用文件扩展名
**[参考网址](https://www.cnblogs.com/bigsai/p/14099154.html)**
---
### HTTP方法
- **GET**
GET请求会向数据库发索取数据的请求,从而来获取资源,该请求就像数据库的select操作一样,**只是用来查询数据,不会影响资源的内容**。无论进行多少次操作,结果都是一样的
- **POST**
POST请求向服务器发送数据,但是该请求**会改变数据的内容(新添)**,就像数据库的`insert`操作一样,会创建新的内容,且POST请求的请求参数都是请求体中,其大小是没有限制的
- **PUT**
PUT请求是向服务器端发送数据的, 与POST请求不同的是,PUT请求**侧重于数据的修改 ,就像数据库中update一样**,而POST请求侧重于数据的增加
- **DELETE**
用来**删除资源**,和数据库中`delete`相对应
### 参数填写位置
- **Params(Query 参数)**:用于 GET 请求或其他将参数**附加在 URL 中**的情况。
- **Body(请求体)**:用于 POST、PUT 等请求方法,将参数**包含在请求体**中。
### 常见的请求文件类型及其适用场景
- **application/json**
用途:用于传输结构化数据,如对象和数组。
场景:客户端与服务器之间交换数据时,尤其是在前后端分离的应用中。
- **multipart/form-data**
用途:用于上传文件或包含文件的表单数据。
场景:用户上传图片、视频、文档等文件。
- **application/x-www-form-urlencoded**
用途:用于提交简单的表单数据。
场景:传统的表单提交,数据量较小且不包含文件。
- **application/octet-stream**
用途:用于传输二进制数据。
场景:下载文件或通过 API 提供文件流。
+92
View File
@@ -0,0 +1,92 @@
## Cookie
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 存储在浏览器端的一小段文本数据(通常 4KB 以内) |
| **核心机制** | 浏览器在每次 HTTP 请求中**自动携带**符合域名/路径规则的 Cookie |
| **关键属性** | `HttpOnly`(防 JS 读取)、`Secure`(仅 HTTPS)、`SameSite`(防 CSRF)、`Expires/Max-Age` |
| **定位** | **传输载体,而非认证方案本身**——它负责把令牌(Session ID 或 Token)在客户端和服务器之间来回搬运 |
**优点**:
- 简单成熟,几乎所有 Web 框架默认集成
- 服务端可随时销毁会话,控制力强
- 敏感信息存在服务端,不暴露给客户端
**缺点**:
- 扩展性受限——多服务器需共享 Session(引入 Redis 等外部存储)
- 依赖 Cookie,纯移动端/非浏览器环境不友好
- 容易受到 CSRF 攻击(需额外防护)
## Session
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 服务器端维护的用户会话数据(通常以 Map 结构存在内存、Redis 或数据库中) |
| **认证流程** | 用户登录 → 服务器创建 Session → 将 Session ID 通过 `Set-Cookie` 返回 → 浏览器后续请求自动携带该 Cookie → 服务器通过 Session ID 查找会话数据验证身份 |
| **核心特点** | **有状态**:服务器持有用户上下文,可随时主动吊销 |
| **定位** | 经典的**服务端主导**的认证方式,适合传统 Web 应用和前后端未彻底分离的场景 |
**优点**:
- 简单成熟,几乎所有 Web 框架默认集成
- 服务端可随时销毁会话,控制力强
- 敏感信息存在服务端,不暴露给客户端
**缺点**:
- 扩展性受限——多服务器需共享 Session(引入 Redis 等外部存储)
- 依赖 Cookie,纯移动端/非浏览器环境不友好
- 容易受到 CSRF 攻击(需额外防护)
## JWT
| **本质** | 一段经过签名(或加密)的 JSON 数据,由 Header、Payload、Signature 三部分组成,以 Base64URL 编码 |
| ------------ | ------------------------------------------------------------ |
| **认证流程** | 用户登录 → 服务器验证凭据并签发 JWT → 客户端存储 Token → 后续请求在 `Authorization: Bearer <token>` 头中携带 → 服务器**仅验证签名和有效期**,无需查库 |
| **核心特点** | **无状态**:Token 自身包含用户身份信息,服务器不需要维护会话 |
| **定位** | 现代**前后端分离架构**和**多端(Web/iOS/Android)统一接入**的事实标准 |
**优点**:
- 天然无状态,服务端可随意水平扩展
- 不依赖 Cookie,可用于移动端、小程序等非浏览器环境
- 跨域友好(CORS 场景下比 Cookie 更灵活)
- Payload 可携带少量业务数据,减少数据库查询
**缺点**:
- Token 一旦签发,在过期前无法主动撤销(需引入黑名单/版本号等机制)
- Payload 仅 Base64 编码(非加密),敏感信息绝对不能放入
- Token 体积比 Session ID 大,每次请求都传输有一定带宽开销
## SSO
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 一种架构模式,通过**统一的认证中心(IdP)**,让用户在多个独立系统间共享登录状态 |
| **认证流程** | 用户访问应用A → 重定向到 SSO 认证中心 → 登录并获取令牌 → 带回令牌访问应用A → 再访问应用B时,认证中心识别已登录状态,直接放行 |
| **常见实现** | CAS、SAML、OAuth 2.0 + OpenID Connect |
| **定位** | **企业级、多系统生态**的统一身份认证方案 |
**优点**:
- 用户体验极佳:一次登录,访问所有关联系统
- 集中管理:统一账号体系、权限策略、安全审计
- 安全性提升:减少密码泄露面,可配合多因素认证(MFA)
**缺点**:
- **单点故障风险**:认证中心一旦宕机,所有系统登录受影响
- 实现复杂:涉及协议配置、令牌传递、跨域重定向等
- 对认证中心的高可用要求极高
## OAuth 2.0
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 一种**授权框架**(而非纯粹的认证协议),允许用户授权第三方应用在**不暴露自己密码**的情况下访问受保护资源 |
| **核心角色** | 资源拥有者(用户)、客户端(第三方应用)、授权服务器、资源服务器 |
| **核心流程** | 用户授权 → 第三方获取授权码 → 换取 Access Token → 持 Token 访问资源 |
| **定位** | **第三方接入**和**开放平台**的标准授权方案(微信登录、Google 登录等) |
+83
View File
@@ -0,0 +1,83 @@
## excelize库
读写Excel的Go库
[github](https://github.com/qax-os/excelize)
## 基本用法
### 创建工作簿
```go
f := excelize.NewFile()
defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()
// 创建一个新的Sheet,返回的index为该工作簿的索引
index, err := f.NewSheet("SheetName")
if err != nil {
return fmt.Errorf("创建Sheet失败: %w", err)
}
```
### 打开文件
```go
f, err := excelize.OpenFile(filePath)
if err != nil {
return fmt.Errorf("打开 Excel失败: %w", err)
}
defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()
```
### 写入cell值
```go
f.SetCellValue("SheetName", "B2", 100)
```
### 设置工作簿默认活动工作表
```go
f.SetActiveSheet(index)
```
### 创建或更新excel表
```go
if err := f.SaveAs("Book1.xlsx"); err != nil {
return fmt.Errorf("保存失败: %w", err)
}
```
### 获取指定cell值
```go
cell, err := f.GetCellValue("Sheet1", "B2")
if err != nil {
return fmt.Errorf("读取cell失败: %w", err)
}
fmt.Println(cell)
```
### 获取所有工作簿的cell值(二维数组)
```go
rows, err := f.GetRows("Sheet1")
if err != nil {
return fmt.Errorf("读取失败: %w", err)
}
for _, row := range rows {
for _, colCell := range row {
fmt.Print(colCell, "\t")
}
fmt.Println()
}
```
+476
View File
@@ -0,0 +1,476 @@
> 感觉还是不如typora好使啊,以后买个正版吧
```css
/**
* MarkText One Light Dark 主题 (仿 Typora onelight-dark:https://github.com/caolib/typora-onelight-theme)
*
* 使用方法:
* 在 MarkText 中打开 设置 → 主题 → 自定义 CSS,
* 将本文件全部内容粘贴进去,保存即可。
*
* 配色来源: Typora onelight-dark theme by caolib
* 兼容: MarkText v0.19.x (旧引擎) / v0.20+ (新引擎)
*/
/* ====================================================================
* CSS 变量 — 使用 camelCase 命名 (旧引擎 v0.19.x 兼容)
* ==================================================================== */
:root {
/* —— 编辑器核心 —— */
--themeColor: #5c99f3;
--themeColor90: rgba(92, 153, 243, 0.9);
--themeColor80: rgba(92, 153, 243, 0.8);
--themeColor70: rgba(92, 153, 243, 0.7);
--themeColor60: rgba(92, 153, 243, 0.6);
--themeColor50: rgba(92, 153, 243, 0.5);
--themeColor40: rgba(92, 153, 243, 0.4);
--themeColor30: rgba(92, 153, 243, 0.3);
--themeColor20: rgba(92, 153, 243, 0.2);
--themeColor10: rgba(92, 153, 243, 0.1);
--highlightThemeColor: #e94ab9;
--highlightColor: #ffffff10;
--selectionColor: #3266d0;
--editorColor: #c3c3c3;
--editorColor80: #c3c3c3;
--editorColor60: rgba(195, 195, 195, 0.6);
--editorColor50: rgba(195, 195, 195, 0.5);
--editorColor40: rgba(195, 195, 195, 0.4);
--editorColor30: rgba(195, 195, 195, 0.3);
--editorColor10: rgba(195, 195, 195, 0.1);
--editorColor04: rgba(195, 195, 195, 0.04);
--editorBgColor: #1f1f1e;
--editorAreaWidth: 900px;
--deleteColor: #ff6b6b;
--iconColor: rgba(195, 195, 195, 0.56);
--codeBgColor: #282c34;
--codeBlockBgColor: #282c34;
--footnoteBgColor: rgba(51, 51, 51, 0.5);
--inputBgColor: #3d404e;
--focusColor: #5c99f3;
/* —— 按钮 —— */
--buttonFontColor: #c3c3c3;
--buttonBgColor: #333333;
--buttonBorder: 1px solid #353a45;
--buttonShadow: none;
--buttonFontColorHover: #ffffff;
--buttonBgColorHover: #414855;
--buttonBorderHover: var(--buttonBorder);
--buttonFontColorActive: var(--buttonFontColor);
--buttonBgColorActive: #2a2d35;
--buttonBorderActive: var(--buttonBorder);
--buttonFocusBorder: 1px solid #5c99f3;
--buttonFocusShadow: 0 0 0 1px rgba(92, 153, 243, 0.3);
--buttonPrimaryFontColor: #ffffff;
--buttonPrimaryBgColor: #5c99f3;
--buttonPrimaryBorder: 1px solid #4a8ad9;
--buttonPrimaryShadow: none;
--buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor);
--buttonPrimaryBgColorHover: #4a8ad9;
--buttonPrimaryBorderHover: var(--buttonPrimaryBorder);
--buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor);
--buttonPrimaryBgColorActive: #3d7bc4;
--buttonPrimaryBorderActive: var(--buttonPrimaryBorder);
--buttonPrimaryFocusBorder: none;
--buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px #5c99f3;
/* —— Markdown 元素 —— */
--headingColor: #c3c3c3;
--h1Color: #c3c3c3;
--h2Color: #c3c3c3;
--h3Color: #c3c3c3;
--h4Color: #ffffff;
--h5Color: #c3c3c3;
--h6Color: #c3c3c3;
--blockquoteTextColor: rgba(195, 195, 195, 0.8);
--blockquoteBorderColor: #5c99f3;
--hrColor: #5c99f3;
--linkColor: #e94ab9;
--strongColor: #5c99f3;
--emColor: #e94ab9;
--listMarkerColor: #5c99f3;
--tableBorderColor: #393e49;
/* —— 侧边栏 —— */
--sideBarColor: #c3c3c3;
--sideBarIconColor: var(--iconColor);
--sideBarTitleColor: #c3c3c3;
--sideBarTextColor: #7a7a7a;
--sideBarBgColor: #1f1f1e;
--sideBarItemHoverBgColor: #333333;
--itemBgColor: #333333;
/* —— 浮动层 —— */
--floatFontColor: #c3c3c3;
--floatBgColor: #353a45;
--floatHoverColor: #414855;
--floatBorderColor: #353a45;
--floatShadow: rgba(0, 0, 0, 0.3);
--maskColor: rgba(0, 0, 0, 0.7);
}
/* ====================================================================
* 全局背景 — 统一为 Typora onelight-dark 底色 #1f1f1e
* ==================================================================== */
html, body {
background-color: #1f1f1e;
}
.editor-wrapper {
background-color: #1f1f1e;
}
.editor-component {
background-color: #1f1f1e;
}
#ag-editor-id {
background-color: #1f1f1e;
}
/* ====================================================================
* 滚动条
* ==================================================================== */
::-webkit-scrollbar,
::-webkit-scrollbar-corner {
background: var(--editorBgColor);
}
::-webkit-scrollbar:vertical { width: 8px; }
::-webkit-scrollbar:vertical:hover { width: 10px; }
::-webkit-scrollbar-thumb {
background: rgba(134, 134, 134, 0.27);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* ====================================================================
* 弹窗 (Element UI)
* ==================================================================== */
.v-modal { background: #08090a !important; opacity: 0.65 !important; }
.el-dialog,
.el-dialog.ag-dialog-table {
border-radius: 6px !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
border: 1px solid #353a45 !important;
background: #2a2a29 !important;
}
.el-button:focus { color: var(--buttonFontColor); }
.el-button--primary:focus {
color: var(--buttonPrimaryFontColor);
background: var(--buttonPrimaryBgColor);
}
/* ====================================================================
* 标题栏 / 浮动菜单
* ==================================================================== */
.title-bar .frameless-titlebar-button > div > svg { fill: #c3c3c3; }
.title-bar .frameless-titlebar-minimize:hover,
.title-bar .frameless-titlebar-toggle:hover {
background-color: rgba(195, 195, 195, 0.05) !important;
}
.input-wrapper {
border-radius: 5px !important;
border: 1px solid #353a45 !important;
background: #2a2a29 !important;
}
.ag-front-menu,
.ag-float-wrapper {
box-shadow: 0 4px 8px 0 var(--floatShadow) !important;
}
/* ====================================================================
* 侧边栏
* ==================================================================== */
.side-bar { border-right: 1px solid #333333 !important; }
.left-column ul > li > svg { fill: rgba(195, 195, 195, 0.6) !important; }
.left-column ul > li:hover > svg { fill: #ffffff !important; }
.left-column ul > li.active > svg { fill: #5c99f3 !important; }
/* ====================================================================
* 标签栏
* ==================================================================== */
.editor-tabs {
box-shadow: none !important;
background: var(--editorBgColor) !important;
}
.editor-tabs:after {
position: absolute;
content: '';
border-bottom: 1px solid #333333;
bottom: 0; left: 0; right: 0; z-index: 1;
}
.editor-tabs ul.tabs-container:after {
position: absolute;
content: '';
border-bottom: 1px solid #333333;
bottom: 0; left: 0; right: 0; z-index: 2;
}
.tabs-container > li,
.tabs-container > li.active { background: var(--editorBgColor) !important; }
.tabs-container > li.active {
border: 1px solid #333333;
border-bottom: none;
}
.tabs-container > li.active:after {
top: 0 !important; right: auto !important;
width: 2px !important; height: auto !important;
background: #5c99f3 !important;
}
.tabs-container svg.close-icon #unsaved-circle-icon { fill: #5c99f3; }
/* ====================================================================
* 编辑器内容区 (v0.19.x 旧引擎: 容器是 #ag-editor-id)
* ==================================================================== */
/* ---- 标题 ---- */
#ag-editor-id h1 {
text-align: center;
padding-bottom: 0.3em;
}
#ag-editor-id h2 {
border-bottom: 2px solid var(--themeColor);
}
#ag-editor-id h3 {
border-left: 4px solid var(--themeColor);
padding-left: 8px;
}
#ag-editor-id h4 {
color: #ffffff !important;
background: var(--themeColor);
border-radius: 5px;
width: fit-content;
padding: 2px 12px;
}
/* ---- 粗体 / 斜体 ---- */
#ag-editor-id strong { color: var(--strongColor); font-weight: 700; }
#ag-editor-id em { color: var(--emColor); }
/* ---- 链接 ---- */
#ag-editor-id a {
color: var(--linkColor);
font-weight: 700;
text-decoration: underline;
}
/* ---- 行内代码 ---- */
#ag-editor-id code {
background-color: var(--codeBlockBgColor);
color: #eb4c37;
border-radius: 3px;
padding: 2px 4px;
}
/* ---- 代码块 ---- */
#ag-editor-id :not(pre) > code[class*='language-'],
#ag-editor-id pre:not(.CodeMirror-line),
#ag-editor-id pre[class*='language-'],
#ag-editor-id pre.ag-paragraph {
background: var(--codeBlockBgColor) !important;
border: none !important;
border-radius: 5px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: box-shadow 0.3s ease;
}
/* ---- 引用块 (四面边框 + 左侧加粗) ---- */
#ag-editor-id blockquote {
padding: 10px 10px 10px 20px;
font-size: 0.9em;
border: 2px solid var(--blockquoteBorderColor);
border-left: 5px solid var(--blockquoteBorderColor);
border-radius: 0 5px 5px 0;
background: transparent;
overflow: auto;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
#ag-editor-id blockquote:hover {
background: var(--editorBgColor);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
/* 隐藏引擎默认的 ::before 竖条,改用 border-left */
#ag-editor-id blockquote::before {
content: none;
}
/* ---- 分割线 ---- */
#ag-editor-id hr {
height: 2px;
background-color: var(--hrColor);
}
/* ---- 表格 ---- */
#ag-editor-id table tr th::before,
#ag-editor-id table tr td::before {
border-color: var(--tableBorderColor);
}
/* ---- 图片 ---- */
#ag-editor-id img {
border-radius: 10px;
display: block;
margin: 0 auto;
max-width: 100%;
}
/* ---- 任务列表复选框 ---- */
#ag-editor-id li.ag-task-list-item {
list-style-type: none;
position: relative;
}
#ag-editor-id li.ag-task-list-item > input[type='checkbox'] {
position: absolute;
cursor: pointer;
width: 16px; height: 16px; top: 0.1em;
margin: 0; left: -24px;
transform-origin: center;
transition: all 0.2s ease;
appearance: none;
-webkit-appearance: none;
}
#ag-editor-id li.ag-task-list-item > input[type='checkbox']::before {
content: '';
width: 16px; height: 16px;
box-sizing: border-box;
display: inline-block;
border: 2px solid var(--editorColor50);
border-radius: 2px;
background-color: var(--editorBgColor);
position: absolute; top: 0; left: 0;
transition: all 0.2s ease;
}
#ag-editor-id li.ag-task-list-item > input[type='checkbox']:checked::before {
border-color: transparent;
background-color: var(--themeColor);
}
#ag-editor-id li.ag-task-list-item > input[type='checkbox']::after {
content: '';
transform: rotate(-45deg) scale(0);
width: 9px; height: 5px;
border: 2px solid #fff;
border-top: none; border-right: none;
position: absolute; display: inline-block;
top: 1px; left: 5px;
transition: all 0.2s ease;
}
#ag-editor-id li.ag-task-list-item > input[type='checkbox']:checked::after {
transform: rotate(-45deg) scale(1);
}
/* ---- 段落悬停 ---- */
#ag-editor-id p.ag-paragraph {
padding: 5px;
margin: 0;
border-radius: 5px;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
#ag-editor-id p.ag-paragraph:hover {
background: #141414;
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.3);
}
/* ---- 已完成任务文字 ---- */
#ag-editor-id li.ag-task-list-item > input.ag-checkbox-checked ~ * {
color: var(--editorColor50);
}
/* ---- 列表标记 ---- */
#ag-editor-id ul > li::marker,
#ag-editor-id ol > li::marker {
color: var(--listMarkerColor);
}
/* ---- 搜索高亮 ---- */
.ag-highlight {
background: #ff0 !important;
color: red !important;
border-radius: 3px;
padding: 0 2px;
height: auto !important;
line-height: inherit !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
/* ---- 表格表头 ---- */
#ag-editor-id table tr th {
background: var(--editorColor04);
}
#ag-editor-id table tr:first-child td {
background: var(--editorColor04);
}
/* ---- 脚注 ---- */
#ag-editor-id sup.md-footnote {
color: var(--strongColor);
font-weight: 700;
}
/* ---- 前置图标 ---- */
.ag-front-icon {
fill: var(--editorColor30);
}
/* ====================================================================
* CodeMirror 源码模式 (v0.19.x 旧引擎)
* ==================================================================== */
.source-code {
background: var(--editorBgColor);
}
.source-code .CodeMirror {
color: var(--editorColor);
background: transparent;
}
.source-code .CodeMirror-gutters {
background: transparent;
border-right-color: #333333;
}
.source-code .CodeMirror-linenumber {
color: var(--editorColor30);
}
.source-code .CodeMirror-cursor {
border-left-color: var(--themeColor);
}
.source-code .CodeMirror-activeline-background {
background: var(--editorColor04);
}
.source-code .CodeMirror-selected {
background: var(--selectionColor) !important;
}
/* CodeMirror 语法高亮色 — 参考 onelight 配色 */
.cm-s-one-dark .cm-comment { color: #7f848e; font-style: italic; }
.cm-s-one-dark .cm-keyword { color: #c678dd; }
.cm-s-one-dark .cm-operator { color: #56b6c2; }
.cm-s-one-dark .cm-builtin { color: #e5c07b; }
.cm-s-one-dark .cm-atom { color: #d19a66; }
.cm-s-one-dark .cm-number { color: #d19a66; }
.cm-s-one-dark .cm-def { color: #61afef; }
.cm-s-one-dark .cm-string { color: #98c379; }
.cm-s-one-dark .cm-string-2 { color: #e06c75; }
.cm-s-one-dark .cm-variable { color: #e06c75; }
.cm-s-one-dark .cm-variable-2 { color: #e5c07b; }
.cm-s-one-dark .cm-variable-3 { color: #d19a66; }
.cm-s-one-dark .cm-type { color: #e5c07b; }
.cm-s-one-dark .cm-meta { color: #e06c75; }
.cm-s-one-dark .cm-attribute { color: #d19a66; }
.cm-s-one-dark .cm-property { color: #e06c75; }
.cm-s-one-dark .cm-qualifier { color: #e5c07b; }
.cm-s-one-dark .cm-tag { color: #e06c75; }
.cm-s-one-dark .cm-link { color: #c678dd; }
.cm-s-one-dark .cm-header { color: #61afef; }
.cm-s-one-dark .cm-quote { color: #98c379; }
/* ====================================================================
* Mermaid/图表 SVG 暗色适配
* ==================================================================== */
figure[data-role] svg rect[fill='#ffffff'] { fill: var(--editorBgColor); }
figure[data-role] svg rect[stroke='#000000'] { stroke: var(--editorColor); }
figure[data-role] svg text[fill='#000000'] { fill: var(--editorColor); }
figure[data-role] svg path[stroke='#000000'] { stroke: var(--editorColor); }
figure[data-role] svg path[fill='#ffffff'] { fill: var(--editorBgColor); }
figure[data-role] svg path[fill='#000000'] { fill: var(--editorColor); }
figure[data-role] svg use[fill='black'] { fill: var(--editorColor); }
figure[data-role] svg use[fill='#000000'] { fill: var(--editorColor); }
```
+72
View File
@@ -0,0 +1,72 @@
# 正则表达式
正则表达式是一种用于匹配和操作文本的强大工具,用于描述要匹配的文本模式。
## 匹配字符
### 普通匹配字符
| 字符 | 描述 |
| :------- | :----------------------------------------------------------- |
| `[xyz]` | 匹配 `[xyz]` 中列出的所有字符。例如 `[xyz]`,能够匹配目标的所有 x、y、z 字符。 |
| `[^xyz]` | 匹配 `[^xyz]` 中列出的字符外的所有字符。例如 `[^xyz]`,能够匹配目标的除去 x、y、z 外的所有字符。 |
| `[A-Z]` | `[A-Z]` 表示一个区间,匹配所有大写字母;`[a-z]` 匹配所有小写字母;`[0-9]` 匹配所有数字。 |
| `[\s\S]` | 匹配所有字符(包括换行符)。`\s` 匹配所有空白符(包括换行),`\S` 匹配所有非空白符(不包括换行),两者合并即可匹配任意字符。 |
| `.` | 匹配除换行符 `\n`、`\r` 之外的任何单个字符,等价于 `[^\n\r]`。 |
| `\w` | 匹配字母、数字、下划线,等价于 `[A-Za-z0-9_]`。 |
| `\d` | 匹配任意一个阿拉伯数字(0 到 9),等价于 `[0-9]`。 |
### 非打印字符
| 字符 | 描述 |
| :---- | :----------------------------------------------------------- |
| `\cx` | 匹配由 x 指明的控制字符。例如,`\cM` 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一,否则将 c 视为一个原义的 'c' 字符。 |
| `\f` | 匹配一个换页符,等价于 `\x0c` 和 `\cL`。 |
| `\n` | 匹配一个换行符,等价于 `\x0a` 和 `\cJ`。 |
| `\r` | 匹配一个回车符,等价于 `\x0d` 和 `\cM`。 |
| `\s` | 匹配任何空白字符,包括空格、制表符、换页符等,等价于 `[ \f\n\r\t\v]`。注意 Unicode 正则表达式会匹配全角空格符。 |
| `\S` | 匹配任何非空白字符,等价于 `[^ \f\n\r\t\v]`。 |
| `\t` | 匹配一个制表符,等价于 `\x09` 和 `\cI`。 |
| `\v` | 匹配一个垂直制表符,等价于 `\x0b` 和 `\cK`。 |
### 特殊字符
| 特殊字符 | 描述 |
| :------- | :----------------------------------------------------------- |
| `$` | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 `$` 也匹配 `\n` 或 `\r`。要匹配 `$` 字符本身,请使用 `\$`。 |
| `( )` | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 `\(` 和 `\)`。 |
| `*` | 匹配前面的子表达式零次或多次。要匹配 `*` 字符本身,请使用 `\*`。 |
| `+` | 匹配前面的子表达式一次或多次。要匹配 `+` 字符本身,请使用 `\+`。 |
| `.` | 匹配除换行符 `\n` 之外的任何单字符。要匹配 `.` 本身,请使用 `\.`。 |
| `[` | 标记一个中括号表达式的开始。要匹配 `[`,请使用 `\[`。 |
| `?` | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 `?` 字符本身,请使用 `\?`。 |
| `\` | 将下一个字符标记为特殊字符、原义字符、向后引用或八进制转义符。例如,`\\` 匹配 `\`。 |
| `^` | 匹配输入字符串的开始位置。在方括号表达式中使用时,表示不接受方括号内的字符集合(取反)。要匹配 `^` 字符本身,请使用 `\^`。 |
| `{` | 标记限定符表达式的开始。要匹配 `{`,请使用 `\{`。 |
| `|` | 指明两项之间的一个选择。要匹配 `|`,请使用 `\|`。 |
### 限定符
| 字符 | 描述 |
| :------ | :----------------------------------------------------------- |
| `*` | 匹配前面的子表达式零次或多次。 |
| `+` | 匹配前面的子表达式一次或多次。 |
| `?` | 匹配前面的子表达式零次或一次,等价于 `{0,1}`。例如,`do(es)?` 可以匹配 "do" 和 "does",但不能匹配 "dog"。 |
| `{n}` | n 是一个非负整数,匹配确定的 n 次。例如,`o{2}` 不能匹配 "Bob" 中的 o,但是能匹配 "food" 中的两个 o。 |
| `{n,}` | n 是一个非负整数,至少匹配 n 次。例如,`o{2,}` 不能匹配 "Bob" 中的 o,但能匹配 "foooood" 中的所有 o。`o{1,}` 等价于 `o+`,`o{0,}` 等价于 `o*`。 |
| `{n,m}` | m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,`o{1,3}` 将匹配 "fooooood" 中的前三个 o。`o{0,1}` 等价于 `o?`。 |
## 常用正则表达式
| 正则表达式 | 匹配内容 |
| -------------------------- | ---------- |
| `[\u4e00-\u9fa5]` | 中文字符 |
| `\d{3}-\d{8}|\d{4}-\{7,8}` | 国内手机号 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
+9
View File
@@ -0,0 +1,9 @@
[Typora onelight 主题](https://github.com/caolib/typora-onelight-theme?tab=readme-ov-file#4%E5%85%B3%E4%BA%8E%E5%AD%97%E4%BD%93)
[Typora自定义主题](https://theme.typora.io/doc/zh/Write-Custom-Theme/)
> 通过自定义的CSS变量来实现
[CSDN增强助手](https://chromewebstore.google.com/detail/csdn%E5%A2%9E%E5%BC%BA%E5%8A%A9%E6%89%8B/fpgaealcfgjbodghojppfmohfcoajled)
> 能够智能屏蔽CSDN搜索结果、屏蔽其广告、自动展开代码