Files
杭州明婳科技 ffcb1a77b9 docs: 注释/文档一致性审查修复(A/B/C 三档共 23 项)
对 60 个非测试源文件做文档/注释/代码一致性审查,修复 23 项。全部为注释/文档
改动 + 1 处死代码删除,无运行逻辑变更,无 breaking change。

A 档·实质错误(10 项,会误导下游):
- app.go: closeResources 三连契约注释统一修正--failAfterInit 走
  rollbackReplacedResources 不走 closeResources,closeResources 仅供 doShutdown 复用
- database/manager.go: RandomPicker 注释从过时的 rand.Intn/issue 54899 改为 crypto/rand
- model/base.go: datetime(0) 与“亚秒精度”矛盾,改为 datetime(6)
- cache/keybuilder.go: 三处示例下划线分隔符改为冒号(与默认分隔符 : 一致)
- repository/repository.go: Delete 接口注释从绝对“软删除”改为条件性;
  UpdateFields 示例移除把字段名当 WHERE 条件的误用写法
- middleware/csrf.go: CSRFToken 注释从“用于 API 模式”改为 Cookie/双重提交模式说明
- examples/full/main.go: POST /users 补上“需 Authorization”标注;删除 _ = app 死代码
- utils/random.go: RandInt/RandInt64 补 min==max 边界说明(对齐 RandIntSecure 写法)

B 档·导出符号注释缺失/不完整(6 项):
- ws/ws.go: 5 个 Type* 常量、4 个 Err* 变量补注释
- console/console.go: 5 个 Level 常量补注释
- jwt/jwt.go: 2 个 Blacklist 常量补注释
- router/router.go: 包级 GroupWithMiddlewareGroup 补注释
- middleware/ratelimit.go: NewRateLimiter 补 panic 条件与 Stop 生命周期说明

C 档·描述不完整(7 项):
- utils/strings.go Substr、convert.go CalcPageCount/CalcOffset、datetime.go
  StartOfMonth/EndOfMonth、file.go CopyFile、validator.go IsChinese、crypto.go HashFile
  补全边界/默认行为/返回值语义
- response/error.go: WithDetail 补 nil 接收者行为说明

验证:go build / go vet / go test -race 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:47:12 +08:00

127 lines
3.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"bytes"
"crypto/md5" // #nosec G501 -- exported checksum helper for non-password/non-security use; callers needing security should use SHA256/HMAC.
"crypto/sha1" // #nosec G505 -- exported checksum helper for legacy/non-security use; callers needing security should use SHA256/HMAC.
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"hash"
"io"
"os"
)
// MD5 计算字符串的 MD5 哈希值
// 注意: 不应用于密码存储
func MD5(s string) string {
return MD5Bytes([]byte(s))
}
// MD5Bytes 计算字节数组的 MD5 哈希值
func MD5Bytes(data []byte) string {
// #nosec G401 -- MD5 helper is retained for legacy checksums, not password storage or security decisions.
h := md5.New()
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// SHA1 计算字符串的 SHA1 哈希值
// 注意: 不应用于密码存储
func SHA1(s string) string {
// #nosec G401 -- SHA1 helper is retained for legacy checksums, not password storage or security decisions.
h := sha1.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// SHA256 计算字符串的 SHA256 哈希值
func SHA256(s string) string {
h := sha256.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// SHA256Bytes 计算字节数组的 SHA256 哈希值
func SHA256Bytes(data []byte) string {
h := sha256.New()
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// HashFile 流式计算文件的哈希值,返回 hex 编码字符串。
//
// newHash 须返回全新的 hash.Hash 实例(如 sha256.New),本函数不复用它。
// Open 或读取失败时返回错误。M-F 修复:改为流式 io.Copy(h, f),不再经 ReadFile 把整文件
// 读入内存——原实现大文件 OOM。流式哈希内存占用恒定、可处理任意大小文件,与 hash.Hash
// 的 io.Writer 接口契合。调用方负责选择哈希算法(如 sha256.New)。
func HashFile(path string, newHash func() hash.Hash) (string, error) {
// #nosec G304 -- generic hashing helper intentionally opens caller-provided trusted local paths.
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := newHash()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// Base64Encode Base64 编码
func Base64Encode(data []byte) string {
return base64.StdEncoding.EncodeToString(data)
}
// Base64Decode Base64 解码
func Base64Decode(s string) ([]byte, error) {
return base64.StdEncoding.DecodeString(s)
}
// Base64URLEncode URL 安全的 Base64 编码
func Base64URLEncode(data []byte) string {
return base64.URLEncoding.EncodeToString(data)
}
// Base64URLDecode URL 安全的 Base64 解码
func Base64URLDecode(s string) ([]byte, error) {
return base64.URLEncoding.DecodeString(s)
}
// Nl2br 将换行符替换为 <br> 标签
func Nl2br(s string, isXhtml bool) string {
var br string
if isXhtml {
br = "<br />"
} else {
br = "<br>"
}
var buf bytes.Buffer
runes := []rune(s)
length := len(runes)
for i, r := range runes {
switch r {
case '\n':
// \n\r(罕见顺序)作为一个换行处理;普通 \n 单独处理。
// N4:原条件含 r == '\r' 半,但本 case 内 r 恒为 '\n',该半恒假,已清理。)
if i+1 < length && runes[i+1] == '\r' {
buf.WriteString(br)
continue
}
buf.WriteString(br)
case '\r':
// \r\n 由上面的 \n 分支处理(\r 在此跳过);单独 \r 作为一个换行。
if i+1 < length && runes[i+1] == '\n' {
continue
}
buf.WriteString(br)
default:
buf.WriteRune(r)
}
}
return buf.String()
}