基于微信小程序的 家庭囤货保质期管理

背景与动机 家庭囤货是常态,但管理不善就会变成「买完忘、过期扔、重复买」的死循环。冷藏室里冻了一年的肉、柜子深处过期的调味料,是每个家庭都会遇到的场景。 市面上已有的保质期管理工具大多是 SaaS 模式,需要注册账号、连接云端。但这类工具的核心场景是个人或家庭内部使用,为了记几个日期去搭一套后端,不仅成本高,还有隐私顾虑。有没有更轻量的办法? 答案是:纯本地微信小程序,零服务器成本。 功能设计 商品录入 扫码录入:使用 wx.scanCode 扫描商品条码,自动获取条码编号 手动回填:当扫码无法获取完整信息时,用户自行填写品名、分类、购入日期、保质期 库存看板 以列表或卡片形式展示所有商品,支持两种排序维度: 按过期时间:即将过期的排最前 按分类:同类商品聚合展示 到期提醒 首页看板高亮标记:即将过期(黄色)和已过期(红色) 小程序启动时自动检查:通过 App.onShow 或页面 onLoad 遍历所有商品,计算过期状态并展示汇总提示 条码识别演进 当前版本扫码后只拿到条码编号,需要用户手动补充品名。商业版计划接入 LLM 识别方案:拍摄商品包装 → OCR 提取文字 → LLM 结构化输出(品名、品牌、保质期),一步完成录入。 技术实现 数据模型 商品是核心实体,字段设计如下: 字段 类型 说明 code string 条码编号 (EAN-13) name string 商品名称 category string 分类(食品、日用品等) quantity number 数量 purchaseDate string 购入日期 (YYYY-MM-DD) expiryDate string 过期日期 (YYYY-MM-DD) notes string 备注 分类采用预置 + 自定义模式。预置分类包括:食品、饮品、调味品、日用品、个人护理、药品、其他。 数据持久化方案 这是整个项目最核心的技术决策。 约束条件:零云服务、零服务器成本、用户无感、数据不能轻易丢失。 微信小程序的两种本地存储 存储方式 限制 清理缓存时 清理全部数据时 wx.setStorageSync 总上限 10MB,单 key 1MB ❌ 丢失 ❌ 丢失 文件系统 (wx.env.USER_DATA_PATH) 总上限 200MB ✅ 保留 ❌ 丢失 关键区别在于:用户最常见的操作是「清理缓存」(清 Storage),而文件系统的数据不受影响。「清理全部数据」才同时清除两者,但这是用户主动销毁数据的操作,任何本地 App 都无法避免。 ...

May 24, 2026

Alpine Image

背景 公司使用的 alpine 镜像进行过网络参数调优 新项目 使用了 cgo (librdkafka-go-bindings) 冲突 想要在该项目中使用调优过的 alpine 镜像 该镜像不具备编译源代码能力 librdkafka-go-bindings 需要编译时开启 cgo 结果 通过 多阶段编译 方式解决上面问题 使用 golang:alpine 环境 作为编译阶段的底包 apk 安装 build-base vim git 等包组和实用工具 编译阶段产物 复制到最终阶段 (使用公司调优的 alpine) 形成了通用的 多阶段编译方案 (伸手党最爱) 源码放置 相关 Dockerfile Makefile ci 配置 编译路径 just build 感想 解决这个问题 使用的 go 库自带电池是非常贴心的 大大降低了 折腾编译依赖库的成本 对比同组的巨巨 手工编译依赖的代码库 我真是 一部到位 使用一个好的 底包也有很大关系 alpine 自带的包管理 使得我能够以最低成本调整出编译代码的必要环境 前期对问题的了解 也很重要 问题识别不清楚 很容易抓不到核心矛盾

June 26, 2020

Go Module 与 私有库

由于公司 vcs 设置缘故 项目基本存在于 subgroup 下 且 import 路径也有属于 project 的 subdir 这就需要公司的 vcs 能够适应 ?go-get=1 访问 subdir 或 subgroup 时能给出正确的 clone 地址 问题背景 公司 vcs 系统使用 gitlab-ce 10.6.5 版本 源码 module 保存在 subgroup 和 subdir 下 vcs 没有 https 端点 调查范围 module get 的工作方式 gitlab 响应逻辑 正常工作的必要条件 认证 联通 编译 实际工作 服务搭建与配置 证书与签名 英文参考 自签名证书 # 1.生成私钥 $ openssl genrsa -out server.key 2048 # 2.生成 CSR (Certificate Signing Request) $ openssl req -new -key server.key -out server.csr # 3.生成自签名证书 $ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt 自建 CA 签名证书 # 1.创建 CA 私钥 $ openssl genrsa -out ca.key 2048 # 2.生成 CA 的自签名证书 $ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt # 3.生成需要颁发证书的私钥 $ openssl genrsa -out server.key 2048 # 4.生成要颁发证书的证书签名请求,证书签名请求当中的 Common Name 必须区别于 CA 的证书里面的 Common Name $ openssl req -new -key server.key -out server.csr # 5.用 2 创建的 CA 证书给 4 生成的 签名请求 进行签名 $ openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt 工具访问与尝试 模拟访问 CURL 模拟 POSTMAN 模拟 HTTPie 模拟 go mod 访问 源码阅读与分析 gitlab 源码 Go Middleware 部分 go mod 源码 Module Lookup 部分 总结 外部评价 内部评价 附录 多级证书签名 # 生成根 CA 并自签(Common Name 填 RootCA) $ openssl genrsa -des3 -out keys/RootCA.key 2048 $ openssl req -new -x509 -days 3650 -key keys/RootCA.key -out keys/RootCA.crt # 生成二级 CA(Common Name 填 SecondCA) $ openssl genrsa -des3 -out keys/secondCA.key 2048 $ openssl rsa -in keys/secondCA.key -out keys/secondCA.key $ openssl req -new -days 3650 -key keys/secondCA.key -out keys/secondCA.csr $ openssl ca -extensions v3_ca -in keys/secondCA.csr -config /etc/pki/tls/openssl.cnf -days 3650 -out keys/secondCA.crt -cert keys/RootCA.crt -keyfile keys/RootCA.key # 生成三级 CA(Common Name 填 ThirdCA) $ openssl genrsa -des3 -out keys/thirdCA.key 2048 $ openssl rsa -in keys/thirdCA.key -out keys/thirdCA.key $ openssl req -new -days 3650 -key keys/thirdCA.key -out keys/thirdCA.csr $ openssl ca -extensions v3_ca -in keys/thirdCA.csr -config /etc/pki/tls/openssl.cnf -days 3650 -out keys/thirdCA.crt -cert keys/secondCA.crt -keyfile keys/secondCA.key # 使用三级 CA 签发服务器证书 $ openssl genrsa -des3 -out keys/server.key 2048 $ openssl rsa -in keys/server.key -out keys/server.key $ openssl req -new -days 3650 -key keys/server.key -out keys/server.csr $ openssl ca -in keys/server.csr -config /etc/pki/tls/openssl.cnf -days 3650 -out keys/server.crt -cert keys/thirdCA.crt -keyfile keys/thirdCA.key build -x 使用 go build -x 能看到更多 go module build 期间更多的细节 ...

February 27, 2020

Go Module 与 私有库 (二)

现状 公司当前使用 GitLab 版本 为 10.6.5 使用 Go Module 拉取 GitLab 中 个人目录下的项目 能够正常工作 使用 Go Module 拉取 GitLab 中 SubGroup/SubDirect 不能够正常工作 go get 解析到的地址为 gitlab.supos.ai/$GROUP/$SUBGROUP 用于拉取项目目录 解析到的地址有误 不能进行后续动作 问题 背景 go get 查找 repo root 方式 go get -u -v gitlab.supos.ai/supos/datalake/common/log get "gitlab.supos.ai/supos/datalake": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake.git"} at //gitlab.supos.ai/supos/datalake?go-get=1 get "gitlab.supos.ai/supos/datalake/common": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake/common", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake/common.git"} at //gitlab.supos.ai/supos/datalake/common?go-get=1 get "gitlab.supos.ai/supos/datalake/common/log": found meta tag get.metaImport{Prefix:"gitlab.supos.ai/supos/datalake/common", VCS:"git", RepoRoot:"http://gitlab.supos.ai/supos/datalake/common.git"} at //gitlab.supos.ai/supos/datalake/common/log?go-get=1 get "gitlab.supos.ai/supos/datalake/common/log": verifying non-authoritative meta tag go: finding gitlab.supos.ai/supos/datalake/common/log latest gitlab.supos.ai/supos/datalake/common/log 由 log 可见 查找 repo root 的过程是根据 路径级别 递归查找 当进入 repo 的 subdir 时 返回的 RepoRoot 仍旧指向 repo 而非 其 subdir go get 携带认证信息的方式 源码位置 ...

February 27, 2020

Go Module 与 私有库 (问题分析篇)

由于公司 vcs 设置缘故 项目基本存在于 subgroup 下 且 import 路径也有属于 project 的 subdir 这就需要公司的 vcs 能够适应 ?go-get=1 访问 subdir 或 subgroup 时能给出正确的 clone 地址 问题背景 公司 vcs 系统使用 gitlab-ce 10.6.5 版本,源码 module 保存在 subgroup 和 subdir 下,vcs 没有 https 端点。 调查范围 module get 的工作方式 gitlab 响应逻辑 正常工作的必要条件 认证 联通 编译 实际工作 服务搭建与配置 证书与签名 英文参考 自签名证书 # 1.生成私钥 $ openssl genrsa -out server.key 2048 # 2.生成 CSR (Certificate Signing Request) $ openssl req -new -key server.key -out server.csr # 3.生成自签名证书 $ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt 自建 CA 签名证书 # 1.创建 CA 私钥 $ openssl genrsa -out ca.key 2048 # 2.生成 CA 的自签名证书 $ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt # 3.生成需要颁发证书的私钥 $ openssl genrsa -out server.key 2048 # 4.生成要颁发证书的证书签名请求 $ openssl req -new -key server.key -out server.csr # 5.用 CA 证书给签名请求进行签名 $ openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt 工具访问与尝试 模拟访问 使用 CURL、POSTMAN、HTTPie 模拟 ?go-get=1 请求进行验证。 ...

February 27, 2020

go module 访问私有库 (gitlab@11.10.4)

开发环境 配置 推荐 编译环境升级 go 1.13+ 启动 GOMOD 模式 设置 GO1111MODULE go env -w GO111MODULE=on 配置公有库依赖管理 设置 GOPROXY go env -w GOPORXY=http://10.30.52.66:3000,direct 配置私有库依赖管理 设置 GOPRIVATE go env -w GOPRIVATE=$PRIVATE_REPO_HOST 设置 Git over HTTP(s) 免密 在 gitlab 注册后从 web 界面 获取 acceess token (编译环境 选只读; 开发环境 选读写) 通过 下述方式 进行免密配置后 不需要进行 ssh 证书添加 touch ~/.git-credentials echo "http://$PRIVATE_REPO_USERNAME:$$PRIVATE_REPO_TOKEN@$PRIVATE_REPO_HOST" >> ~/.git-credentials echo "https://$PRIVATE_REPO_USERNAME:$$PRIVATE_REPO_TOKEN@$PRIVATE_REPO_HOST" >> ~/.git-credentials git config --global credential.helper store 设置 Module 命令认证信息 go mod 命令的认证信息 仅能从 主机登陆协议配置 $HOME/.netrc 中获取 ...

February 16, 2020

Init

release: build @cd zhangli2946.github.io && git add . && git commit -m "update" && git push build: hugo -D

February 16, 2020

使用 prometheus 监控应用性能

引入:Pull 模型的服务监控 Pull 模型 由 Prometheus 向所有已知的 target 发送 http get $HOST:$PORT/metrics http get prometheus:9090/metrics # HELP prometheus_tsdb_wal_page_flushes_total Total number of page flushes. # TYPE prometheus_tsdb_wal_page_flushes_total counter prometheus_tsdb_wal_page_flushes_total 5759 # HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code. # TYPE promhttp_metric_handler_requests_total counter promhttp_metric_handler_requests_total{code="200"} 1134 promhttp_metric_handler_requests_total{code="500"} 0 promhttp_metric_handler_requests_total{code="503"} 0 # HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served. # TYPE promhttp_metric_handler_requests_in_flight gauge promhttp_metric_handler_requests_in_flight 1 # HELP prometheus_tsdb_wal_truncate_duration_seconds Duration of WAL truncation. # TYPE prometheus_tsdb_wal_truncate_duration_seconds summary prometheus_tsdb_wal_truncate_duration_seconds{quantile="0.5"} NaN prometheus_tsdb_wal_truncate_duration_seconds{quantile="0.9"} NaN prometheus_tsdb_wal_truncate_duration_seconds{quantile="0.99"} NaN prometheus_tsdb_wal_truncate_duration_seconds_sum 0 prometheus_tsdb_wal_truncate_duration_seconds_count 0 prometheus_tsdb_wal_truncations_total 0 # HELP prometheus_tsdb_compaction_chunk_range_seconds Final time range # TYPE prometheus_tsdb_compaction_chunk_range_seconds histogram prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="102400"} 1 prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="1.6384e+06"} 594 prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="6.5536e+06"} 2838 prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="+Inf"} 2838 prometheus_tsdb_compaction_chunk_range_seconds_sum 4.489241e+09 prometheus_tsdb_compaction_chunk_range_seconds_count 2838 可视化效果 grafana prometheus 数据类型 与 指标定义 数据类型 计数器 单调增加 示数器 随意增减 分布 分段统计 _bucket{le=""} 计量 _sum 计数 _count 摘要 分位统计 {quantile=""} 计量 _sum 计数 _count 指标定义 示例:Apdex score 原文 ...

February 16, 2020