跳转至内容
  • 0 赞同
    10 帖子
    25 浏览
    L
    在线支付状态变更耗时总结:应该是连接的服务器有差异,之前慢的时候连接的服务器是 api.dollar.la ;最新连接的服务器是 api.loda.net; 下图是最新 域名发布之后的 监控日志截图 [图片] [image: 1769237852771-f529c79f-4e98-4ccb-9eea-1c60c88f215a-%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_17690627679170.png]
  • LT-133: Staging 环境 Web API 忽略 API_VERSION_FOLDER

    WindSurf
    7
    0 赞同
    7 帖子
    10 浏览
    Z
    我这个对话其实很简洁,因为我的工作区,设置了“Rule”如图: [image: 1769019936255-ed38b3f8-9ecd-4352-a499-a059c25c1d52-%E5%9B%BE%E7%89%87-resized.png]
  • Vibe Coding 实战规范(工程向 · v1.0)

    WindSurf
    1
    0 赞同
    1 帖子
    1 浏览
    Z
    《Vibe Coding 实战规范(工程向 · v1.0)》 这不是宣传稿,是工程规范,是你可以拿去: 发到 Outline / Wiki 作为技术文化基线 作为 Code Review、架构评审时的“共同语言” 公司工程规范 Vibe Coding 实战规范(Engineering Edition · v1.0) 适用对象:全体程序员 / 架构师 / 技术负责人 目标:提升整体工程效率、系统质量与工程幸福感 0️⃣ 核心原则(先统一思想) 原则一句话版 人负责决策,AI负责实现;人对结果负责,AI不背锅。 明确三点共识 Vibe Coding ≠ 不写代码 Vibe Coding ≠ AI 自动生成即可上线 Vibe Coding = 更高级的工程分工方式 1️⃣ 人与 AI 的职责边界(非常重要) 人(程序员)的职责 你必须亲自完成: 业务理解与问题定义 系统边界划分 架构选型(语言 / 框架 / 存储 / 通信) 安全、性能、可维护性判断 最终代码 Review 与合并决策 任何“我没看,是 AI 写的”都不可接受 AI 的职责 AI 被允许、且鼓励用于: 样板代码生成 CRUD / DTO / Mapper / 配置类 单元测试、Mock、测试用例扩展 文档初稿、README、接口说明 重构尝试、替代方案推演 AI 是“超级实习生 + 编译器 + 搜索引擎”的合体 2️⃣ 标准 Vibe Coding 工作流(必须遵守) Step 1:先写「意图文档」,再写代码 任何功能开始前,至少写清楚以下 4 点(可 10 行内): ## Intent - 我要解决什么问题? - 为什么现在要做? ## Scope - 本次做什么 - 明确不做什么 ## Constraints - 技术约束(语言/框架/版本) - 性能/安全/兼容性要求 ## Done Definition - 什么状态算“完成” 这是给 AI 的,也是给未来的你和同事的。 Step 2:用自然语言“指挥”AI,而不是贴代码 不推荐: “帮我写一个 XXX 的代码” 推荐: “在以下约束下,设计一个可扩展方案,先给结构,再给示例实现” “这个模块未来可能要支持多租户,请预留扩展点” Step 3:AI 输出 ≠ 完成,必须人工校验 必须人工检查的点: 是否违反公司技术规范 是否引入多余复杂度 是否有安全 / 性能隐患 是否真的解决了最初的问题 AI 的第一版,默认是“草稿”。 Step 4:允许推翻,鼓励重来 Vibe Coding 的核心优势: 推翻方案的心理成本极低 不要“舍不得已经写好的代码” 一句标准心态: “这版不对,换个 vibe 再来一轮。” 3️⃣ 代码规范(Vibe Coding 特有) 3.1 生成代码必须“可读、可解释” 禁止: 晦涩的黑魔法 AI 自创的奇怪模式 没人看得懂的“炫技代码” 要求: 命名清晰 结构直观 注释解释「为什么」,不是「做了什么」 3.2 必须留下“人类痕迹” 以下至少满足一项: 架构说明注释 关键决策点的理由说明 与 AI 方案不同的人工修改痕迹 这不是给领导看,是给未来维护的人看。 4️⃣ Code Review 新规则(必须调整) Review 不再关注: “这行代码是不是你亲手写的?” Review 必须关注: 是否符合设计意图 是否符合系统长期演进方向 是否存在被 AI 忽略的边界条件 是否可以更简单 一句话总结: Review 的对象是“方案质量”,不是“作者是谁”。 5️⃣ 对个人能力的真实要求(说清楚,不画饼) Vibe Coding 不会降低要求,反而更高: 你需要提升的是: 抽象能力 表达能力(对人 & 对 AI) 架构理解 系统思维 判断力 不会思考的人,用 AI 只会更快地产生垃圾。 6️⃣ 常见误区(明确禁止) “AI 写的,应该没问题” “先跑起来再说” “我看不懂但能用” “反正不是我写的” 责任永远属于合并代码的人。 7️⃣ 我们为什么要这样做? 不是为了: 追热点 显得先进 少招人 而是为了: 把有限的工程师时间,用在最有价值的地方 减少无意义的重复劳动 让工程师更多做“思考型工作” 8️⃣ 结语(可以原样引用) Vibe Coding 不是捷径,而是把工程师从“体力劳动”中解放出来的一种新分工方式。 我们不降低质量标准,只提升效率上限。 AI 不是替代者,而是放大器。 最终负责系统成败的,仍然是人。
  • Vibe Coding 常见质疑 · 官方 FAQ(工程师版)

    WindSurf
    1
    0 赞同
    1 帖子
    1 浏览
    Z
    Vibe Coding 常见质疑 · 官方 FAQ(工程师版) 适用人群: 对 AI 写代码持怀疑态度 / 有经验的程序员 / 担心被“降级”的同事 目标: 消除误解,而不是强行说服 Q1:Vibe Coding 不就是“让 AI 写代码,人偷懒”吗? 短答:不是。 长答: Vibe Coding 的核心不是“谁敲代码”,而是: 谁负责判断,谁承担责任。 在 Vibe Coding 里: AI 负责:生成、补全、枚举、试错 人负责:架构、边界、取舍、验收 如果你只是把需求丢给 AI,不看、不改、不负责—— 那不是 Vibe Coding,那是失职。 一句话反驳: “你让 IDE 自动补全代码,也没说你在‘偷懒’。” Q2:AI 写的代码质量很差,根本不敢用 这是一个“阶段性正确,但方向性错误”的判断。 事实是: AI 第一次给你的代码,通常不完美 但它第 N 次修改,速度远超人类 Vibe Coding 的正确用法是: 你给结构 + 约束 AI 给实现 你指出问题 AI 局部重写 你不是“相信 AI”,而是“驱动 AI”。 反问一句就够了: “你现在写的第一版代码,是完美的吗?” Q3:那我们是不是以后都不用学底层、不用懂原理了? 恰恰相反。 在 Vibe Coding 时代: 不懂原理的人,更容易被 AI 带偏 懂原理的人,才能快速判断 AI 是否在胡说 AI 不会替你判断: 这个锁有没有死锁风险 这个事务边界对不对 这个设计将来能不能演进 结论: Vibe Coding 会淘汰“只会写代码但不懂系统的人”, 而不是淘汰真正的工程师。 Q4:这样会不会让新人更水?大家水平被拉平? 这是一个非常典型、但方向完全相反的担忧。 真实情况是: 人的类型 在 Vibe Coding 下 架构能力强 输出速度指数级提升 会拆问题 能快速驾驭 AI 只会照抄 更快暴露问题 不会表达需求 AI 也帮不了 AI 放大的不是“努力”,而是“能力结构”。 一句话结论: 差距不会消失,只会被加速放大。 Q5:那以后还考不考“代码能力”?我是不是白练了这么多年? 不会,而且你练的东西终于值钱了。 过去: 代码能力 ≈ 手速 + 记忆 + 经验 现在: 代码能力 ≈ 系统感 抽象能力 约束表达 代码审美 风险意识 你过去十年练的那些: “一眼看出不对劲” “这个地方迟早要炸” “这种写法后面肯定难维护” 这些,AI 给不了新人。 Q6:这是不是管理层为了“压人效 / 裁人”的手段? 这是一个必须正面回答的问题。 我们的立场是: 如果只是为了裁人,那根本不需要 Vibe Coding。 Vibe Coding 的真实目标是: 减少低价值重复劳动 让工程师把精力用在真正需要“人”的地方 提高系统整体吞吐,而不是榨干个人 一句现实但诚实的话: “不拥抱 AI 的团队,不会因为拒绝而变安全。” Q7:那我该怎么开始?是不是要一下子全换? 不需要,也不允许“一刀切”。 推荐的低风险起点: 新模块 / 新脚手架 文档生成 单元测试补全 重构草案 多方案对比 禁止的做法: 核心逻辑直接无审查上线 把 AI 当权威 不写上下文、不做约束 Q8:一句话总结:为什么我要接受 Vibe Coding? 因为它本质上是在说一句话: “程序员不应该把生命浪费在 AI 已经能干得更快的事情上。” 最后给所有工程师的一句话 Vibe Coding 不是命令,也不是 KPI。 它是一种选择: 选择把自己从“代码劳工”升级为“系统设计者” 选择让 AI 替你消耗体力,而不是消耗热情 选择站在未来,而不是被未来追着跑
  • 为什么我们要在公司推动 Vibe Coding

    WindSurf
    1
    0 赞同
    1 帖子
    12 浏览
    Z
    为什么我们要在公司推动 Vibe Coding 最近在推动 Vibe Coding 的过程中,有同事认真地问我一句: “什么是 Vibe Coding?” 这个问题本身,非常好。 说明大家不是盲从,而是在思考:这是不是又一个管理层发明的流行词? 我的结论很明确: Vibe Coding 不是概念包装,而是一次程序员生产方式的代际升级。 一、先说人话:什么是 Vibe Coding? 一句话版本: Vibe Coding = 人负责“方向、判断、品味”,AI负责“实现、推演、补全”。 不是: 让 AI 取代程序员 也不是“不会写代码的人指挥 AI 写代码” 而是: 程序员仍然是架构师 AI 变成了一个永不疲倦、不会抱怨、可随时重来的“超级搭档” 你不再从「敲每一行代码」开始,而是从: 我要解决什么问题? 系统的边界在哪里? 哪些地方必须我亲自判断? 哪些地方可以放心交给 AI? 开始。 二、Vibe Coding 和“传统写代码”的本质区别 传统 Coding 模式(我们太熟了) 需求 → 设计 → 写代码 → 查文档 → Debug → 改 → 再 Debug 特点: 人力成本高 情绪损耗大 重复劳动多 创造力被消耗在“体力活”上 Vibe Coding 模式 意图 → 结构 → 约束 → AI 实现 → 人校验 → 快速迭代 变化在于: 维度 传统 Coding Vibe Coding 人的角色 代码工人 架构师 / 决策者 时间消耗 写 & 查 想 & 验 重复劳动 人反复做 AI 反复做 出错成本 高 低(重来几乎无成本) 思考层级 语法级 系统级 你不是写得更少了,而是站得更高了。 三、为什么公司必须推动 Vibe Coding? 1️⃣ 因为“代码量”已经不是核心竞争力 今天真正稀缺的不是: 会写 for 循环的人 会拼 CRUD 的人 而是: 能理解业务本质的人 能设计系统边界的人 能把混乱需求转化为清晰结构的人 这些,AI 很难替代;但写代码本身,AI 非常擅长。 2️⃣ 因为我们项目多、变化快、人永远不够 现实是: 项目数量在涨 系统复杂度在涨 人的精力是线性的 Vibe Coding 带来的不是“偷懒”,而是: 更快试错 更早验证 更低心理负担 写废了?删了重来。 方案不对?换个 vibe 再跑一版。 3️⃣ 因为它会放大“高手”的价值,而不是抹平差距 一个常见误解是: “AI 会让大家水平变得一样。” 恰恰相反。 在 Vibe Coding 下: 懂架构的人 → 爆发力指数级提升 会提好问题的人 → 效率碾压 有工程审美的人 → 输出质量拉开差距 AI 不会帮你做“判断”,只会放大你的“判断”。 四、Vibe Coding 对程序员意味着什么? 说一句实在话: 这是近十年来,第一次真正“站在程序员这边”的技术浪潮。 它意味着: 你可以把精力花在“值得骄傲的事情”上 而不是被琐碎实现慢慢榨干热情 它也意味着: 你的价值不再绑定在“手速”和“记忆力” 而绑定在思考深度、系统感、表达能力 五、在我们公司,Vibe Coding 会怎么落地? 不是口号,而是明确方向: 文档优先(意图、背景、约束先写清) 允许试错(允许 AI 方案被推翻) 鼓励对话(人和 AI 的对话本身就是设计过程) 代码不是目的,系统可演进性才是 AI 是工具,不是权威;人是最终责任人。 六、最后一句话 如果你问我一句: “Vibe Coding 值不值得学?” 我的回答是: 这不是‘值不值得’,而是‘早一点,还是晚一点’的问题。 我们选择现在开始,是因为: 我们尊重工程师的时间 尊重思考的价值 也尊重未来已经到来的事实
  • 不是传统 DevOps,而是 Docs-Driven + AI-First Development

    WindSurf
    6
    0 赞同
    6 帖子
    7 浏览
    Z
    供开发人员浏览的文档中心,用什么软件呢?docker-compose部署的?需要OIDC登入吗?能看到历史版本change的细节?
  • 基于分支的完整工作流程示例

    团队公告
    6
    0 赞同
    6 帖子
    42 浏览
    Z
    Git 中常见的三种整合策略简单说明如下(都是把功能分支合进目标分支,只是历史呈现不同): Merge(普通合并) 保留双方原始提交,再生成一个新的合并提交。 优点:历史完整、不改写已有提交;缺点:分支图会出现分叉。 场景:多人协作、需要保留完整记录时的默认做法。 Rebase(变基) 把功能分支的提交“搬到”目标分支最新提交之后,好像一直基于它开发。 优点:历史呈线性,很干净;缺点:会改写提交哈希,容易引发冲突,不适合已推送共享的分支。 Squash(压缩合并) 把功能分支所有提交压成一个提交再合并。 优点:目标分支历史简洁;缺点:功能分支内部的细节提交丢失,回溯困难。 场景:功能分支提交非常琐碎、只想留一个总结提交时使用。 如果你没有特别需求,建议直接使用 Merge,操作相对安全,也符合大多数团队习惯。 下一步请告诉我要合并的功能分支是哪个(比如 feature/LT-94-friendly-timeout-error),我就按 Merge 流程帮你在 develop、master 分支各合并一次并提交。
  • .csproj文件中版本号的管理和包引用的追溯功能

    团队公告
    2
    0 赞同
    2 帖子
    2 浏览
    Z
    结论 你的目标(消灭硬编码版本、沉淀“包→项目”映射)很合理,且能同时服务到 CI 合规与项目管理洞察。 现有模板(nuget-component-linux.yml、nuget-component-hybrid.yml)已经通过 shared/package-version-check.yml 引入了脚本级检查;要实现更强的“建议 + 追踪”能力,需要把 version-writer 的上下游信息贯通起来。 你的方案(在 version-writer Controller 里新增扫描、入库 API,流水线 setup 时调用)是可行的,但还需要一些边界和数据结构设计,才能让 CI 运行成本、Runner 镜像更新、数据库一致性都可控。 我参考了“gitlab-ci 三层架构 / Key 规则”的记忆,确保改动方向遵循 LT-78 特殊规则。 可行性评估 CI 模板现状 nuget-component-linux.yml 只是桥接到 .NET 8.0 模板;nuget-component-hybrid.yml 桥接到 .NET 4.8.1 模板。无论哪个入口,最终都在 setup 阶段跑 setup_version、setup_global_var,之后 build/test/push/update-version 全部共用。要加逻辑就放在 setup_global_var 阶段最自然(已有 download-directory-builds-props.sh 等脚本)。 硬编码版本建议 借助我们已在 runner 镜像里扩展的 check-package-versions.sh + props 注释,现在本地扫描即可找到变量建议。若想“更全局、可查询”,需要把扫描结果保存到数据库,再由 version-writer 或一个小型 API 服务来提供读取引用。 包引用登记 每个 pipeline 扫描 .csproj → 生成“项目/仓库/分支 + 包名 + 当前变量名 + 版本”记录,写入统一的 API/DB。后续查询就能反查“包在哪些项目中被使用”。 关键点:需要全局唯一的项目标识(比如仓库路径、commit SHA、版本号),避免数据冲突;同时要考虑 pipeline 并发写入和旧数据淘汰策略。 建议的实现路径 API 设计(version-writer 服务或新微服务) POST /packages/usage:提交单次扫描结果(包→项目→变量名→版本)。 POST /packages/variables:记录 version-writer 生成的“包-变量”对(含环境、产品线)。 GET /packages/{name}/usage:提供查询接口给你提到的“项目经理/开发人员”界面使用。 Runner / CI 侧改动 在 setup_global_var 阶段新增脚本: 使用 PROJECT_PATHS 或自动发现 .csproj,调用现有 check-package-versions.sh(或提炼公共函数)生成 JSON/CSV; 调用上述 API 上传扫描结果; 若发现硬编码版本,继续沿用现有脚本的“精准建议 + 可选失败”逻辑。 这样 runner 镜像只需增加一个“上传脚本 + curl 工具”,风险可控。 version-writer Controller 扩展 在 update-version 入口里,把“模块名 → 变量名 → 版本”同步写到 API,作为权威数据源; 同时确保在 props 注释里输出包名/变量名,供 CI 快速匹配。 查询侧 由你的“另外的项目”直接访问 API / 读库,展示“包 → 影响的项目/仓库/版本/上次检测时间”等信息。 也可以反查“项目 → 使用的所有包及变量”,辅助评审。 风险与建议 数据一致性:Pipeline 失败或中途中止时,已有的数据是否回滚?建议 API 支持“覆盖”策略(用 commit SHA + job ID 作为主键)。 权限与网络:Runner 镜像要能访问 version-writer API(内网 or VPN),必要时配置 token/证书。 扫描性能:一次 CI 扫几十个 csproj,如果都在 setup 阶段执行,注意不要显著拖慢 pipeline;可以缓存 Directory.Build.props 下载、并发处理。 落地顺序(建议分阶段): version-writer 提供 “包 变量” API,并在 update-version 阶段写入; Runner 在 setup 阶段扫描 .csproj → 上传“包引用快照”; Explorer/Query 项目读取数据,提供查询界面; 最终再考虑对 CI 做更严格的阻断策略(例如 packages 未登记则禁止发布)。 下一步(可选) 确定 version-writer API 的接口设计/存储方案(SQL/NoSQL/Elastic)。 在 runner 镜像里新增一个 report-package-usage.sh,复用 check-package-versions.sh 提供的解析能力。 在 CI 模板(shared/nuget-setup.yml)里接入该脚本,确保所有引用模板的项目都能上报。 搭建查询界面(可先用简单的 web/Excel 输出,验证体验)。 如需我帮忙设计 API、数据库 schema 或落地脚本,随时说。
  • 0 赞同
    1 帖子
    6 浏览
    Z
    当前体系梳理 部署组件 Blazor 前端服务由 StoreManageBlazorModule 负责,Program 初始化时启动 Serilog+Autofac,加载 StoreManageBlazorModule 并构建应用(Program.cs,@Loda.Abp.StoreManage.Blazor/src/Loda.Abp.StoreManage.Blazor/Program.cs#14-55)。 HTTP API 层由 StoreManageHttpApiHostModule 支撑,该模块依赖 EF Core、Redis 缓存、Redis 分布式锁、Hangfire 以及 RabbitMQ 等,构成主 API/后台任务平台(@Loda.Abp.StoreManage/src/Loda.Abp.StoreManage.HttpApi.Host/StoreManageHttpApiHostModule.cs#59-417)。 域层与数据层 功能模块(基础、商品、订单、库存、财务、客户、流水号等)通过 StoreManageDomainModule 聚合,并启用多租户/本地化/审核等通用能力(@Loda.Abp.StoreManage/src/Loda.Abp.StoreManage.Domain/StoreManageDomainModule.cs#32-93)。 EF Core 模块注册了主/租户 DbContext 并默认启用所有实体的仓储,同时固定使用 SQL Server(@Loda.Abp.StoreManage/src/Loda.Abp.StoreManage.EntityFrameworkCore/EntityFrameworkCore/StoreManageEntityFrameworkCoreModule.cs#24-75)。 Blazor 端与 HttpApi 端共享同样的应用层/域层配置,保证统一业务逻辑。 可能的性能瓶颈点 数据库资源 EF Core + Hangfire + 各模块的默认仓储会对 SQL Server 产生较多并发查询/事务(上述模块在 EntityFrameworkCoreModule 中默认 includeAllEntities: true,@...#54-67)。t2.large 4 核 8G 内存,如果请求数集中、查询复杂、缺乏索引/分页,会引发锁、延迟。建议通过慢查询/blk_lock 分析热点 SQL。 后台任务与数据访问叠加 Hangfire 使用同一 SQL Server 存储,调度间隔为 15 秒、事务超时 1min,默认 WorkerCount 取 Math.Max(Environment.ProcessorCount * 3, 10),若任务频繁跑或与前端请求共享 DB 连接,会抢占资源(@...#385-416)。可考虑专用 Job DB 或调低 WorkerCount。 缓存/锁使用 实际部署中依赖 Redis(数据保护、分布式锁),若 Redis 网络延迟或连接配置不佳,也会影响 API 响应。建议检查 Redis:Configuration 连接稳定性与连接池。 Blazor SignalR/静态文件 Blazor Server(默认)依赖 SignalR,会与 API 同机共享 CPU/网络,若客户端数多或大文件(blazor-global-styles.css 等)频繁加载,应开启 CDN 静态资源或引入 Service Worker 缓存(模块中通过 ConfigureBundles 注册,@...#212-235)。 建议的调查方向 收集 SQL Server 端慢查询/等待(sys.dm_exec_requests + sys.dm_os_waiting_tasks)确认是否索引/分页问题。 检查 StoreManageDbContext 池设置、默认仓储查询是否产生 N+1(可通过 EF Core 日志定位)。 确认 Hangfire job 与 API 是否争抢连接数,必要时降低队列并发、改用独立 DB。 验证 Redis 连接数/延迟,保证 ConnectionMultiplexer 是单例,避免频繁新建。 若前端慢是 SignalR 连接数问题,可考虑将 Blazor 迁移为 Wasm+API 或增加节点,并用 CDN 提升静态资源交付。 如需我帮你定位某个具体模块/配置文件,或分析日志/监控数据,请再提供相关信息。
  • 在曼谷部署进销存

    Ansible
    13
    1
    0 赞同
    13 帖子
    9 浏览
    Z
    我看基本没问题了,请为我生成git语句签入。
  • 工作区和全局的Rules

    WindSurf
    4
    0 赞同
    4 帖子
    2 浏览
    Z
    签入之后,Gitlab收到了“提交”: [image: 1768231443017-5f1981c3-e5d3-491e-adeb-4fca8dc8cb51-image-resized.png]
  • Docker 部署自动日志收集功能

    团队公告
    5
    0 赞同
    5 帖子
    4 浏览
    A
    === 容器状态 === CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bc54e3be67b7 docker-sz.loda.net.cn/docker-pos/rider/api:2855.2026.0112.59831 "dotnet Loda.Distrib…" 18 seconds ago Up 18 seconds 0.0.0.0:8071->80/tcp distribution-centre-api-test 44b32ace9727 docker-sz.loda.net.cn/docker-pos/api:2855.2026.0112.59828 "dotnet Loda.Abp.Sto…" 7 minutes ago Up 7 minutes 0.0.0.0:8041->8080/tcp pos-api-test 48ca6fd5bef4 docker-sz.loda.net.cn/docker-pos/blazor:2855.2026.0110.59707 "dotnet Loda.Abp.Sto…" 2 days ago Up 2 days 0.0.0.0:8031->8080/tcp pos-blazor-test 5a2278b77870 docker-sz.loda.net.cn/docker-pos/auth:2855.2026.0109.59379 "dotnet Loda.Abp.Sto…" 3 days ago Up 3 days 0.0.0.0:8051->8080/tcp pos-auth-test 3a7636d4a907 docker-hk.loda.net.cn/redis:7-alpine "docker-entrypoint.s…" 6 weeks ago Up 7 days 0.0.0.0:56379->6379/tcp, [::]:56379->6379/tcp redis-56379 a417ffcc7aa6 docker-hk.loda.net.cn/redis:7-alpine "docker-entrypoint.s…" 6 weeks ago Up 7 days 0.0.0.0:56380->6379/tcp, [::]:56380->6379/tcp redis-56380 029b4cfc0ab9 rewardplatformweb-reward "dotnet RewardPlatfo…" 4 months ago Up 7 days (unhealthy) 0.0.0.0:44396->80/tcp, [::]:44396->80/tcp reward === 容器资源 === CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS bc54e3be67b7 distribution-centre-api-test 0.78% 184.2MiB / 6.976GiB 2.58% 62.5kB / 162kB 8.19kB / 53.2kB 49 44b32ace9727 pos-api-test 0.06% 435.5MiB / 6.976GiB 6.10% 590kB / 703kB 4.1kB / 152kB 51 48ca6fd5bef4 pos-blazor-test 0.21% 249.2MiB / 6.976GiB 3.49% 6.5MB / 8.44MB 1.02MB / 229kB 33 5a2278b77870 pos-auth-test 0.24% 433.4MiB / 6.976GiB 6.07% 12.2MB / 20.3MB 8.19kB / 1.13MB 33 3a7636d4a907 redis-56379 0.47% 3.57MiB / 6.976GiB 0.05% 52.7kB / 25.3kB 8.97MB / 49.2kB 6 a417ffcc7aa6 redis-56380 0.51% 19.94MiB / 6.976GiB 0.28% 22.9kB / 7.06kB 17.7MB / 0B 6 029b4cfc0ab9 reward 0.05% 393MiB / 6.976GiB 5.50% 941kB / 22.3MB 160MB / 4.1kB 22 === 容器详情 === [ { "Id": "bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9", "Created": "2026-01-12T11:32:44.399418948Z", "Path": "dotnet", "Args": [ "Loda.DistributionCentre.Web.Host.dll" ], "State": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 749357, "ExitCode": 0, "Error": "", "StartedAt": "2026-01-12T11:32:44.464254948Z", "FinishedAt": "0001-01-01T00:00:00Z" }, "Image": "sha256:cc436d5e068a5daa68a27a4ebf4e83df30818d758963e7743beb44c03cfcce1d", "ResolvConfPath": "/var/lib/docker/containers/bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9/resolv.conf", "HostnamePath": "/var/lib/docker/containers/bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9/hostname", "HostsPath": "/var/lib/docker/containers/bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9/hosts", "LogPath": "/var/lib/docker/containers/bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9/bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9-json.log", "Name": "/distribution-centre-api-test", "RestartCount": 0, "Driver": "overlay2", "Platform": "linux", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "docker-default", "ExecIDs": null, "HostConfig": { "Binds": [ "/srv/distribution-centre/logs/api:/app/Logs" ], "ContainerIDFile": "", "LogConfig": { "Type": "json-file", "Config": {} }, "NetworkMode": "bridge", "PortBindings": { "80/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8071" } ] }, "RestartPolicy": { "Name": "unless-stopped", "MaximumRetryCount": 0 }, "AutoRemove": false, "VolumeDriver": "", "VolumesFrom": null, "ConsoleSize": [ 0, 0 ], "CapAdd": null, "CapDrop": null, "CgroupnsMode": "private", "Dns": [], "DnsOptions": [], "DnsSearch": [], "ExtraHosts": null, "GroupAdd": null, "IpcMode": "private", "Cgroup": "", "Links": null, "OomScoreAdj": 0, "PidMode": "", "Privileged": false, "PublishAllPorts": false, "ReadonlyRootfs": false, "SecurityOpt": null, "UTSMode": "", "UsernsMode": "", "ShmSize": 67108864, "Runtime": "runc", "Isolation": "", "CpuShares": 0, "Memory": 0, "NanoCpus": 0, "CgroupParent": "", "BlkioWeight": 0, "BlkioWeightDevice": [], "BlkioDeviceReadBps": [], "BlkioDeviceWriteBps": [], "BlkioDeviceReadIOps": [], "BlkioDeviceWriteIOps": [], "CpuPeriod": 0, "CpuQuota": 0, "CpuRealtimePeriod": 0, "CpuRealtimeRuntime": 0, "CpusetCpus": "", "CpusetMems": "", "Devices": [], "DeviceCgroupRules": null, "DeviceRequests": null, "MemoryReservation": 0, "MemorySwap": 0, "MemorySwappiness": null, "OomKillDisable": null, "PidsLimit": null, "Ulimits": [], "CpuCount": 0, "CpuPercent": 0, "IOMaximumIOps": 0, "IOMaximumBandwidth": 0, "MaskedPaths": [ "/proc/asound", "/proc/acpi", "/proc/interrupts", "/proc/kcore", "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/proc/scsi", "/sys/firmware", "/sys/devices/virtual/powercap" ], "ReadonlyPaths": [ "/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" ] }, "GraphDriver": { "Data": { "ID": "bc54e3be67b7280e7666edc5f7ded99d76211ab947c5c52ad3adf77b826b0bb9", "LowerDir": "/var/lib/docker/overlay2/405cb8c9aa1666804f2c182a7215306363f95f8d2f713e7f5eb755b7d13bc493-init/diff:/var/lib/docker/overlay2/082b1a06e0b75f5906735e830c5b02bad10909e873d21bb4d64095097b071aba/diff:/var/lib/docker/overlay2/3bca4a900387a94b7d43166ad48c413534a07a761e583b1118a2ceff02d66d93/diff:/var/lib/docker/overlay2/b88e36f6375c14437e305a81b2c6512b409c095142d8b03a715fc54bdbc14ca5/diff:/var/lib/docker/overlay2/236a2b85bf0d62b68bf0b644f1f9b142e1edf555ba7ccb478ac501e8b5e03877/diff:/var/lib/docker/overlay2/167a94024e505e7a252fb6173fc4633116b3af9380a28370b16ec6719b7fa3d0/diff:/var/lib/docker/overlay2/318fb2a62244f9fa3144f5a83eaf6373410df98c6b4112b4fc7d247635f7a1ea/diff:/var/lib/docker/overlay2/b32418fcb0903e993b4b79fbb53a09d3436d0710c15992f1c0325b2be49b49e8/diff:/var/lib/docker/overlay2/236684bbdd8e7e3a0e19cc47b5bcf13b3302e317ca9a2912532a46d7d8808fc8/diff:/var/lib/docker/overlay2/95b57c8b5efd1f4e5030e42ee8f4e0d40b98d8a7668a8054ff4bf9c69d99d9eb/diff:/var/lib/docker/overlay2/9e4b53d4f574b7a538da3d5a211ad71f500be70439b464f9d408c45ecc3dcd95/diff:/var/lib/docker/overlay2/913fc948cbabc01d43e5e3d21b50d9fb56c651a663bd34b3b71051fdaea5baeb/diff:/var/lib/docker/overlay2/fda0c16f14d6705c29c54872df539c21d0f641a932ff70bfa1224f56cf467530/diff:/var/lib/docker/overlay2/256584f70b45487c8562f8f38c535f38c70cc555c8434b25f8bcb22685ac308d/diff:/var/lib/docker/overlay2/42e5bceb6f86515a2d61b68bfd268145d62d61e63d553b944ef93f57b1a86404/diff:/var/lib/docker/overlay2/31ef0ef7df042188980f2f6c55553ce2a6110c9f581da00ea308afa142dcda14/diff:/var/lib/docker/overlay2/e5a65937fbbfbd3d27da24393b700e95a66879d9ea968fd19cad32bc811117f8/diff:/var/lib/docker/overlay2/9db579ae7ae6f8a2cdd0931e79b20f4b6c4edea50e62692f401b8421273782fb/diff:/var/lib/docker/overlay2/7583c02e3323c98e545a9a6c7f343700bbbb633f4c856a2a222f8e3652eda60d/diff:/var/lib/docker/overlay2/f13db28022489bf3e63b2aa531757e264e1cb0ad34635afdbaed373bc02a9d83/diff:/var/lib/docker/overlay2/8e089d7b6f69eb33696977c5a36d749c49df43992e06c0e6cf762b1c67722b94/diff:/var/lib/docker/overlay2/64e4ec95d4195213956f64f2bf7d2a37982214b887d4208bbad0d1a826d2014f/diff", "MergedDir": "/var/lib/docker/overlay2/405cb8c9aa1666804f2c182a7215306363f95f8d2f713e7f5eb755b7d13bc493/merged", "UpperDir": "/var/lib/docker/overlay2/405cb8c9aa1666804f2c182a7215306363f95f8d2f713e7f5eb755b7d13bc493/diff", "WorkDir": "/var/lib/docker/overlay2/405cb8c9aa1666804f2c182a7215306363f95f8d2f713e7f5eb755b7d13bc493/work" }, "Name": "overlay2" }, "Mounts": [ { "Type": "bind", "Source": "/srv/distribution-centre/logs/api", "Destination": "/app/Logs", "Mode": "", "RW": true, "Propagation": "rprivate" } ], "Config": { "Hostname": "bc54e3be67b7", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "ASPNETCORE_ENVIRONMENT=test", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "ASPNETCORE_URLS=", "DOTNET_RUNNING_IN_CONTAINER=true", "DOTNET_VERSION=6.0.36", "ASPNET_VERSION=6.0.36", "DOTNET_GENERATE_ASPNET_CERTIFICATE=false", "DOTNET_NOLOGO=true", "DOTNET_SDK_VERSION=6.0.428", "DOTNET_USE_POLLING_FILE_WATCHER=true", "NUGET_XMLDOC_MODE=skip", "POWERSHELL_DISTRIBUTION_CHANNEL=PSDocker-DotnetSDK-Debian-11" ], "Cmd": null, "Image": "docker-sz.loda.net.cn/docker-pos/rider/api:2855.2026.0112.59831", "Volumes": null, "WorkingDir": "/app", "Entrypoint": [ "dotnet", "Loda.DistributionCentre.Web.Host.dll" ], "OnBuild": null, "Labels": {} }, "NetworkSettings": { "Bridge": "", "SandboxID": "a5068e2868bc64451f4fbdf56f6c775a1a55a80d6ae268a919a20d6468c35da0", "SandboxKey": "/var/run/docker/netns/a5068e2868bc", "Ports": { "80/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8071" } ] }, "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "eb35adb6802d8a42c50452cf2acb4d24a894d8218cce11118e85796992a645a9", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.5", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "5a:12:cb:9b:bb:d1", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "MacAddress": "5a:12:cb:9b:bb:d1", "DriverOpts": null, "GwPriority": 0, "NetworkID": "616640d561322f9ace160edec3bba3d6f310eec53f590a3318b099589228104c", "EndpointID": "eb35adb6802d8a42c50452cf2acb4d24a894d8218cce11118e85796992a645a9", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.5", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "DNSNames": null } } } } ] === 端口监听 === State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* LISTEN 0 4096 127.0.0.1:39471 0.0.0.0:* LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 511 0.0.0.0:888 0.0.0.0:* LISTEN 0 4096 0.0.0.0:44396 0.0.0.0:* LISTEN 0 4096 0.0.0.0:56380 0.0.0.0:* LISTEN 0 4096 0.0.0.0:56379 0.0.0.0:* LISTEN 0 4096 0.0.0.0:8071 0.0.0.0:* LISTEN 0 4096 0.0.0.0:8031 0.0.0.0:* LISTEN 0 4096 0.0.0.0:8041 0.0.0.0:* LISTEN 0 4096 0.0.0.0:8051 0.0.0.0:* LISTEN 0 511 0.0.0.0:36379 0.0.0.0:* LISTEN 0 100 0.0.0.0:51435 0.0.0.0:* LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* LISTEN 0 4096 [::]:22 [::]:* LISTEN 0 4096 [::]:44396 [::]:* LISTEN 0 4096 [::]:56380 [::]:* LISTEN 0 4096 [::]:56379 [::]:* === 磁盘空间 === Filesystem Size Used Avail Use% Mounted on tmpfs 715M 5.2M 710M 1% /run efivarfs 256K 19K 233K 8% /sys/firmware/efi/efivars /dev/vda3 99G 42G 53G 45% / tmpfs 3.5G 5.7M 3.5G 1% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock /dev/vda2 197M 6.2M 191M 4% /boot/efi tmpfs 715M 12K 715M 1% /run/user/0 tmpfs 715M 12K 715M 1% /run/user/1004 === 内存状态 === total used free shared buff/cache available Mem: 7143 2718 270 575 5035 4424 Swap: 0 0 0 === OOM事件 === === Docker事件 === 2026-01-12T19:23:20.607064812+08:00 container exec_create: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, com.docker.compose.project.config_files=/home/reward/RewardPlatform/src/RewardPlatform.Web/docker-compose.yaml, com.docker.compose.project.working_dir=/home/reward/RewardPlatform/src/RewardPlatform.Web, com.docker.compose.service=reward, com.docker.compose.version=2.28.1, execID=c8d3a5f8883245901e3c0fb9782d82b054ffb2ecb90c0d00693eba51f0e7f2dd, image=rewardplatformweb-reward, name=reward) 2026-01-12T19:23:20.607167182+08:00 container exec_start: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, com.docker.compose.project.config_files=/home/reward/RewardPlatform/src/RewardPlatform.Web/docker-compose.yaml, com.docker.compose.project.working_dir=/home/reward/RewardPlatform/src/RewardPlatform.Web, com.docker.compose.service=reward, com.docker.compose.version=2.28.1, execID=c8d3a5f8883245901e3c0fb9782d82b054ffb2ecb90c0d00693eba51f0e7f2dd, image=rewardplatformweb-reward, name=reward) 2026-01-12T19:23:50.722054677+08:00 container exec_create: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, com.docker.compose.project.config_files=/home/reward/RewardPlatform/src/RewardPlatform.Web/docker-compose.yaml, com.docker.compose.project.working_dir=/home/reward/RewardPlatform/src/RewardPlatform.Web, com.docker.compose.service=reward, com.docker.compose.version=2.28.1, execID=6398e56e45718f4ac693847a526222ea7b8030992eb5679215f138d5dcab1148, image=rewardplatformweb-reward, name=reward) 2026-01-12T19:23:50.722167378+08:00 container exec_start: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, 2026-01-12T19:32:53.013454020+08:00 container exec_create: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, com.docker.compose.project.config_files=/home/reward/RewardPlatform/src/RewardPlatform.Web/docker-compose.yaml, com.docker.compose.project.working_dir=/home/reward/RewardPlatform/src/RewardPlatform.Web, com.docker.compose.service=reward, com.docker.compose.version=2.28.1, execID=0a338bec297fda57ebfc2e3f12a01d32bb7fa4e814bd5ec2fd13d1f3c1a4678f, image=rewardplatformweb-reward, name=reward) 2026-01-12T19:32:53.013568922+08:00 container exec_start: curl -f http://localhost/health-status 029b4cfc0ab9a4172e12088c4faab4c84235ae86dd7baa30fbf8f879b68ee880 (com.docker.compose.config-hash=fa5720073ce1ebf925849b1e4cbd586052490604987028241836fd0c700bff6f, com.docker.compose.container-number=1, com.docker.compose.depends_on=, com.docker.compose.image=sha256:464e6086f5d1aa2b694e82cbc5b2540f1b20a8791fba5ecb4ba6493951c8daa5, com.docker.compose.oneoff=False, com.docker.compose.project=rewardplatformweb, com.docker.compose.project.config_files=/home/reward/RewardPlatform/src/RewardPlatform.Web/docker-compose.yaml, com.docker.compose.project.working_dir=/home/reward/RewardPlatform/src/RewardPlatform.Web, com.docker.compose.service=reward, com.docker.compose.version=2.28.1, execID=0a338bec297fda57ebfc2e3f12a01d32bb7fa4e814bd5ec2fd13d1f3c1a4678f, image=rewardplatformweb-reward, name=reward) === Done === Mon Jan 12 07:33:05 PM CST 2026
  • 在Visual Studio中编译和启动ABP模块的Blazor Host

    ABP
    2
    7
    0 赞同
    2 帖子
    3 浏览
    Z
    明明在ABP Studio里创建解决方案的时候,勾选了把模块加入”main“,但是实际上是无效的。要对Blazor和Blazor.Client都添加模块的项目引用和依赖才行。 如图: [image: 1768114771058-8.png] [image: 1768114771095-9-resized.png]
  • 在ABP 10.0中创建解决方案

    ABP
    1
    18
    0 赞同
    1 帖子
    1 浏览
    Z
    [image: 1768112184991-1.png] [image: 1768112230429-2.png] [image: 1768112672647-3.png] [image: 1768112672690-4.png] [image: 1768112672718-5.png] [image: 1768112672742-6.png] [image: 1768112672769-7.png] [image: 1768112672795-8.png] [image: 1768112672820-9.png] [image: 1768112672855-10.png] [image: 1768112672883-11.png] [image: 1768112672912-12.png] [image: 1768112672935-13.png] [image: 1768112672962-14.png] [image: 1768112672986-15.png] [image: 1768112673003-16.png] [image: 1768112673029-17.png] [image: 1768112673065-18-resized.png]
  • 安装 Redis 不带密码的步骤

    开发环境搭建
    1
    0 赞同
    1 帖子
    5 浏览
    A
    安装 Redis 不带密码的步骤 在 WSL (Ubuntu) 中执行以下命令安装 Redis: sudo apt update sudo apt install redis-server -y 修改 Redis 配置文件以禁用密码认证: sudo sed -i 's/^requirepass/#requirepass/' /etc/redis/redis.conf 重启 Redis 服务使配置生效: sudo service redis-server restart 验证 Redis 是否正常运行且无需密码: redis-cli ping 如果返回 "PONG" 则表示安装成功且无需密码。
  • 签入代码治理要求(Commit/MR/分支的可追溯治理)

    团队公告
    2
    0 赞同
    2 帖子
    13 浏览
    A
    1. 背景:我们为什么要改流程 近期我们在 CI/CD 与分支治理上做了一轮升级,目标很明确: 提升生产环境安全性:避免“谁改了 master 就把生产一起带崩”的风险 提升可追溯性:每个改动都能对应到一个明确的需求/缺陷 Key(如 ZD-xxx / TG-xxx / REQ-xxx) 在多仓库、多级触发(A->B->C)的情况下保持稳定:避免触发链路分支漂移、遗漏合并 这套升级会增加一些管理动作,但它换来的是可控、可审计、可回滚的发布链路。 2. 现场问题:为什么“我要上生产”会变成“要把所有人都合进去” 我们遇到的典型困惑是: 当前大家都在 develop 上做修改 某个修复已经在 develop 测试通过,现在要发布生产 但 develop 同时包含其他人的改动 一旦用 develop -> master 合并,就会把其他人的改动也带上生产 结果看起来就像“流程绕了一圈,和以前直接改 master 没区别”,甚至更累 这不是某个人的问题,而是**“只有 master+develop 且大家直接改 develop”**这种工作方式的必然结果: develop 会自然变成“所有进行中的改动大杂烩”,不具备按需挑选发布的能力。 3. 根因剖析:为什么会出现“hotfix 从 develop 拉”的错误用法 当大家想紧急发生产时,很自然会做出一个看似合理、实则危险的动作: 从 develop 拉一个 hotfix/* 分支 然后 hotfix/* -> master 发生产 问题在于:如果 hotfix 的基线来自 develop,它本质上等价于“把 develop 发布到生产”。 这就必然把别人还未准备好的改动一起带进去。 关键原则: 生产修复(hotfix)必须从“生产基线”分出来 也就是:hotfix/* 必须从 master 拉,而不是从 develop 拉。 4. 共识目标:我们到底要的是什么制度 我们需要一套“成本不高、不会出错”的制度,解决两个核心问题: 生产发布可控:能明确“这次上生产到底包含哪些提交” 多仓库链路稳定:上游是什么分支,下游就尽量跟随;不跟随也要有可预测的 fallback 换句话说:让 master 真的“受保护”,让生产发布变成一个可管理动作。 5. 建议落地方案(推荐,成本最低且最符合治理闭环) 5.1 分支定位(长期分支 + 临时分支) master(生产基线,受保护) 只允许 MR 合入 对应 production/staging 发布(手动) develop(集成基线,建议受保护) 通过 MR 合入,减少“无意破坏” 对应 test 环境(自动) 允许临时分支(不等于分支爆炸,而是必要的“隔离器”) feature/<KEY>-<slug>:从 develop 拉,合回 develop hotfix/<KEY>-<slug>:从 master 拉,合回 master 发生产 这并不是回到复杂 GitFlow,而是最小集合: “两个长期分支 + 两类临时分支”,用来解决“只发布一部分”的刚需。 5.2 发布路径(把“整合发布”和“紧急修复”分开) 整合发布(develop 当前整体可发布) 走 develop -> master MR staging 验证后,手动 production 紧急修复(develop 不可整体发布) 从 master 拉 hotfix/<KEY> 只把必要提交带过去(见下一节 cherry-pick) hotfix -> master 上生产 回灌:再做一次 master -> develop,避免修复丢失或未来重复出现 5.3 cherry-pick 的角色:解决“只想上其中几个提交” 当修复已经在 develop 做完,但 develop 夹杂其他人改动时: 不再用 develop -> master 解决 而是从 master 拉 hotfix/<KEY>,然后对 develop 上属于本次修复的提交执行 cherry-pick 这样生产只包含你要发布的那几个提交,不会把别人的未完成工作带上去 6. 多仓库下游触发:FANOUT_BRANCH 的意义与补充规则 在多级触发(A->B->C)里,容易出现“分支漂移”: 每一跳都用 CI_COMMIT_BRANCH 触发下游,会导致链路不可预测。 现有文档已定义唯一原则: FANOUT_BRANCH 贯穿整条触发链路 上游传了就沿用;没传就等于 CI_COMMIT_BRANCH 补充建议:同名分支优先 + 不存在则回落 master(可选增强) 当上游是 hotfix/<KEY> 时,下游可能未创建同名分支。理想策略是: 下游存在同名分支:触发同名分支 下游不存在:回落到 master 这类 fallback 要做到“稳定不报错”,通常需要在触发前做一次“分支存在性判断”(脚本/机制层面),避免触发直接失败。是否引入该增强,视团队对稳定性与实现成本的取舍决定。 7. 这套制度能解决什么、代价是什么 能解决 生产发布可控:不会再“合并一次把所有人带上生产” 责任边界清晰:谁的改动、哪个 Key、进了哪些仓库、是否已合 master 一目了然 发布更安全:master 真正成为“受保护的生产基线” 需要付出的代价 合并与回灌变成日常管理工作(但这是“可控发布”的必要成本) 要求改动尽量小步快跑,否则合并冲突会指数级上升 8. 执行清单(请团队统一遵守) 分支来源 hotfix/* 必须从 master 拉 feature/* 从 develop 拉 发布生产 非紧急:develop -> master(前提是 develop 已声明为可发布集合) 紧急:hotfix/* -> master(必要提交用 cherry-pick 精准带入) 回灌 hotfix 发布后必须 master -> develop 治理约束 分支名、commit、MR 标题必须带合法 Key(ZD/TG/REQ) master 必须受保护,只允许 MR 9. 下一步(建议) 把以上规则固化进团队文档(branch-rules) 在 CI 中增加最小校验(例如:hotfix MR 目标必须是 master;关键作业基于分支类型区分环境) 结合“按 Key 聚合查询”工具:每天/每周形成“哪些 Key 已合 master、哪些仍在 develop/feature”的报告,降低遗漏风险 如果你希望我把这篇公告进一步“贴近你们仓库的现实参数”(比如:当前只允许 master/develop 还是允许 feature/hotfix;哪些环境 job 是自动/手动;是否需要下游分支 fallback),你回复两句话即可: 你们最终是否允许临时分支 feature/*、hotfix/*? 下游触发 fallback(同名分支优先,不存在回落 master)要不要做成强制能力?
  • 下游触发分支规则(FANOUT_BRANCH 单原则)

    团队公告
    2
    0 赞同
    2 帖子
    6 浏览
    A
    下游触发分支选择:现象、根因与最终方案(仅 develop/master) 背景与目标 我们在 NuGet 组件发布流水线中使用下游触发(trigger downstream pipeline)来联动发布/同步多个项目。 核心目标是: 让下游触发分支选择可预测、可维护、可审计。 避免因为 GitLab Token 权限差异导致的“误判分支不存在”“偶发失败”等不稳定行为。 适配跨项目、跨实例同步(common-ci 同步到多个 GitLab 实例)的场景。 现象(问题表现) 在 trigger_downstream_pipeline 作业中出现以下典型问题: 现象 A:作业过早触发 流水线还未完成 push/update 等步骤,下游就被触发。 现象 B:下游明明存在分支,却提示分支不存在 例如下游有 develop,但 job 日志显示“分支不存在”。 现象 C:HTTP 401 Unauthorized 使用 CI_JOB_TOKEN 调用 GitLab API 触发下游 pipeline 时返回 401。 说明默认情况下 CI_JOB_TOKEN 不具备跨项目触发的权限。 现象 D:Reference not found / downstream pipeline can not be created trigger 使用的 branch 变量为空或未按预期注入,导致 GitLab 认为 ref 不存在。 根因分析 1. GitLab 原生 trigger: 的能力边界 原生 trigger: 只能触发一个确定的 project + branch。 它不会做“分支不存在则 fallback”这种动态选择。 因此,如果要做“某项目没有分支就改触发另一个分支、后续再恢复原分支”的行为,必须: 预先知道下游是否存在该分支(需要 API),或 直接尝试触发并解析失败原因(需要 API),或 人工/静态配置规则(每项目一个开关),或 制定组织规范(所有项目都具备统一分支)。 2. 跨项目权限与 Token 复杂度 如果使用 API 触发下游(/api/v4/projects/:id/pipeline): CI_JOB_TOKEN 在多数场景下对跨项目 API 权限不足(401)。 使用 GITLAB_API_TOKEN(Project/Group/Personal Access Token)可以解决,但引入: Token 分发与轮换成本 安全风险与合规审计成本 多实例同步时的配置复杂度 3. 变量注入顺序与 artifacts 依赖 我们使用 dotenv artifacts 在 setup 阶段写入变量(如 FANOUT_BRANCH、DOWNSTREAM_BRANCH)。 如果 trigger job 没有正确 needs 对应的 setup job(且 artifacts: true),就可能拿不到变量,导致: $DOWNSTREAM_BRANCH 为空 trigger 触发时报 Reference not found 方案对比(做过的选择) 方案 1:智能分支(API + fallback) 思路:优先触发“根分支”,失败再 fallback 到 develop/master。 优点:理论上能实现更复杂的链路行为。 缺点: 需要跨项目 API 权限,CI_JOB_TOKEN 往往不够(401) Token 配置与安全治理成本高 失败原因多样(401/404/网络/权限策略),可维护性差 方案 2:自建服务(分支查询/代理触发) 思路:将 GitLab token 收拢到内部服务,CI 只调用内部服务。 优点:可控、安全面可收敛。 缺点:引入额外系统: 部署与运维成本 高可用/限流/审计等需求 增加链路复杂度 方案 3(最终采用):最简单方案(无 API,仅 develop/master) 结论:采用方案 3。 只在 develop/master 两者之间选择分支。 在 setup 阶段一次性确定“触发链根分支”,并通过 dotenv 跨项目传递。 trigger 使用 GitLab 原生 trigger:,不使用 API,不依赖 token。 该方案的关键前提:所有参与链路的项目必须同时存在 develop 和 master 分支。 这项前提换来的是: 不需要任何额外权限与 token 行为稳定、可预测 维护成本最低 最终方案设计 1. 根分支归一化(setup 阶段) 在 setup_version(.version_setup)阶段: 读取触发源:FANOUT_BRANCH(上游传递)或 CI_COMMIT_BRANCH(当前) 归一化为: master/main/hotfix/* → master 其他 → develop 写入 dotenv: FANOUT_BRANCH=<develop|master> DOWNSTREAM_BRANCH=<develop|master>(如果用户未显式指定) 2. 下游触发(trigger 阶段) 在 .trigger-downstream 中: 使用 GitLab 原生: trigger.project: $DOWNSTREAM_PROJECT trigger.branch: $DOWNSTREAM_BRANCH 同时向下游继续传递: FANOUT_BRANCH FANOUT_FROM STOP_FANOUT 并通过 needs 确保拿到 setup 阶段的 dotenv: needs: setup_global_var (artifacts: true) 代码改动点(common-ci) shared/version-setup.yml 在 version.env 中写入归一化后的 FANOUT_BRANCH,并默认填充 DOWNSTREAM_BRANCH。 nuget/7-trigger-downstream.yml 恢复为原生 trigger: needs 增加 setup_global_var(带 artifacts: true)以确保 DOWNSTREAM_BRANCH 可用 使用与约束 组织规范(必须执行) 所有参与链路的仓库必须同时存在 develop 与 master 分支。 项目侧配置(可选) 在 .gitlab-ci.yml 中配置: DOWNSTREAM_PROJECT:下游项目路径 一般无需配置 DOWNSTREAM_BRANCH,默认由上游归一化规则决定。 常见问题(FAQ) Q1:为什么不继续做“智能分支 + fallback”? 因为需要 API 触发或分支判断,而跨项目 token 权限与安全治理成本过高,且多实例同步场景下不可控因素更多。 Q2:为什么会出现 Reference not found? 多数情况下是 trigger job 没有拿到 setup 阶段的 dotenv(变量未注入),导致 branch 为空/错误。 Q3:这个方案还能实现“某项目只有 master,但后续又想回到 develop”吗? 不能。 不使用 API 的前提下无法判断下游是否存在某分支,因此无法对“每一跳”做动态选择。 要支持这种行为,必须回到 API 方案或引入项目级配置。
  • LT-66:Redis基础操作的GetObject增强限制

    团队公告
    1
    8
    0 赞同
    1 帖子
    9 浏览
    Z
    本地调试web form程序,容易登入重定向循环。错误的原因几乎都是因为localhost网站调用的Redis配置和Passport站点的Redis不同。 这里包含两种情况: 1)localhost的Reids根本就是无效的; 2)和Passport不一致导致返回的对象为空。 于是我们修改基础方法,Redis获取时如果是连接故障、超时、密码错误,就抛出异常。 [image: 1767791589611-9-resized.png] [image: 1767792084183-10-resized.png] [image: 1767792151983-11-resized.png] [image: 1767792230126-12.png] 在本地开发测试效果 1)故意把Redis的端口号写错误,报错如下: [image: 1767792352836-13-resized.png] 2)故意把Redis的密码写错,报错如下: [image: 1767796927755-14-resized.png] 3)故意把Redis的密码为空,报错如下: [image: 1767797016112-15-resized.png] 4)故意把Redis的DefaultDB配置成错误的数字,报错如下: 这个改动是在yee-web-security仓库,修改了LoginPage基类实现的。 [image: 1767799499867-16.png] 5)Redis的DefaultKey配置错误,暂时没有影响。
  • MkDocs是什么?

    软件工程
    1
    0 赞同
    1 帖子
    1 浏览
    Z
    下面我按**“你现在的认知起点”来解释,不假设你知道 MkDocs,也不站在“工具爱好者”的角度,而是站在一个要管理 500+ 仓库、异地团队、AI 高频产出**的技术负责人的视角。 一、MkDocs 是什么?一句话先给你结论 MkDocs = 把 Markdown 目录,自动变成“可搜索、可导航、可长期维护”的文档网站的引擎。 它不是用来“写文档”的,而是用来: 把你已经写好的 Markdown,组织、发布、持续演进。 MkDocs 不解决什么 不负责写 Markdown(AI / 人写) 不负责讨论(NodeBB 才是) 不负责权限(GitLab / Pages / 反向代理) MkDocs 专门解决什么 Markdown → 结构化网站 统一目录、侧边栏、全文搜索 多人协作、版本可追溯 自动发布(CI 驱动) 二、MkDocs 在你这个场景里的“真实角色” 你现在的问题是: AI 每天生成大量 md 团队不在一个城市 项目 500+ 不能让大家“翻帖子考古” 在这个体系里: 组件 角色 Markdown 原材料(AI 产出) GitLab 单一事实源(谁改了什么) MkDocs 把“散乱 md”变成“可用知识库” GitLab Pages 稳定发布 NodeBB 通知 + 讨论 MkDocs 是“知识仓库引擎”,不是“交流工具”。 三、MkDocs 到底长什么样(不用你想象) MkDocs 生成的是一个典型的技术文档站: 左侧:目录树(按你定义) 右侧:正文(Markdown 渲染) 顶部:搜索框(全文搜索) URL 稳定、可引用、可长期保存 这类站点的特点是: 3 个月后还能看懂 1 年后链接还有效 新人能顺着目录自学 AI 写的文档不会“沉没” 这正是论坛/聊天工具永远做不好的事情。 四、MkDocs 的工作方式(你会秒懂) 1️⃣ 你只关心这个目录 docs/ index.md now/ README.md daily/ 2026-01-05.md decisions/ adr-0001-auth.md 2️⃣ MkDocs 只做一件事 读取 docs/ → 生成一个静态网站 3️⃣ 唯一的“配置文件”:mkdocs.yml site_name: 团队知识库 nav: - 首页: index.md - 我在做什么: now/README.md - 日报: - 2026-01-05: daily/2026-01-05.md - 架构决策: decisions/ theme: name: material 你改的不是网页,而是目录结构。 五、为什么不用 NodeBB 直接发帖?这是关键问题 你这个问题问得非常好,而且80% 的团队一开始都会走错这一步。 NodeBB 发帖,看起来很“直接”,但在你规模下会出灾难 我直接给你对比结论: 1️⃣ 信息“寿命”完全不同 维度 NodeBB 帖子 MkDocs 文档 可见期 3~7 天 3~5 年 查找方式 搜索关键词 目录 + 搜索 链接稳定性 易被淹没 永久链接 结构化 强 论坛适合“现在讨论什么”,不适合“我们最终决定了什么”。 2️⃣ 500 项目 + NodeBB = 考古现场 你可以想象一下半年后: “那个关于 AuthServer 改造的帖子在哪?” “好像在 NodeBB?还是微信群?” “是哪个版本的决策?” 而 MkDocs 的模式是: 架构决策 ├─ adr-0001-auth-server.md ├─ adr-0002-oidc-nodebb.md 不是靠记忆,是靠路径。 3️⃣ NodeBB 不适合“版本演进” 你是 .NET / ABP / 基础设施重构型团队: v1 决策 v2 推翻 v3 新方案 NodeBB 的时间线是线性的,而文档是演进式的。 MkDocs + Git 的优势是: diff 看变更 commit 说明为什么变 旧决策可追溯 4️⃣ AI 产出在 NodeBB 会“失控” AI 非常擅长: 写长文 写方案 写对比 但 论坛: 没有审阅门槛 没有目录约束 没有生命周期管理 结果一定是: 信息爆炸 → 噪音淹没 → 再也没人看 MkDocs 强迫你做一件事: “这个文档,应该放在哪个位置?” 这是好事,不是负担。 六、那 NodeBB 该怎么用?(正确姿势) 不是不用 NodeBB,而是只让它做它擅长的事: NodeBB = “广播 + 讨论入口” 发布策略建议: NodeBB 帖子永远只做一件事: 告诉大家:“有新内容/新决策/新文档了,在这里” 帖子结构固定化: 标题:📌 架构决策更新:统一 OIDC 登录方案 正文: - 背景:多系统登录混乱 - 结论:统一走 id.loda.net - 影响范围:NodeBB / ERP / Admin - 文档地址:https://docs.xxx.com/decisions/adr-0003-oidc.html 👇 讨论放在本帖 讨论在 NodeBB,结论在 MkDocs。 七、这套模式在“异地 + 高频 AI + 大规模仓库”下为什么稳定 你现在的情况非常典型: AI 写得快 人分散 项目爆炸 历史包袱重 MkDocs 给你的不是“更好看的文档”,而是: 强制沉淀(不写就不存在) 低摩擦协作(Markdown + Git) 可审计演进(谁、何时、为何) 新人可自学(不依赖你反复解释) 而 NodeBB: 负责“告诉大家发生了什么” 负责“大家怎么看” 八、一句话总结给你 NodeBB 是“现在聊什么”,MkDocs 是“我们最终怎么做”。 没有 MkDocs,你的 AI 产出一定会在 3 个月内失效。 下一步我建议你做什么(非常具体) 先 不管 500 个项目 只用 MkDocs 管三类内容: now/(你在干什么) daily/(你每天产出的 AI 结果) decisions/(你真正拍板的事情) NodeBB 只发“变更通知帖 + 链接” 如果你愿意,下一步我可以直接给你一份: 极简 mkdocs.yml(你不需要理解) 3 个模板 md(now / daily / adr) 一个 CI:push → Pages 自动更新 → NodeBB/企业微信通知 你只需要照着填内容,不需要“学 MkDocs”。
  • Gitlab Pages是什么?

    软件工程
    1
    0 赞同
    1 帖子
    1 浏览
    Z
    下面把 GitLab Pages 按“你从未用过、但要在自建 GitLab 上把它做成团队文档统一入口”的角度,系统讲清楚:它是什么、怎么工作、你需要准备什么、怎么和 MkDocs/集中站点/NodeBB/企业微信配合。 1. GitLab Pages 是什么 GitLab Pages = GitLab 自带的静态网站托管能力。 你把网站的静态文件(HTML/CSS/JS/图片等)通过 CI 产出成一个 artifact,GitLab Pages 就会把它对外提供成一个网站。(GitLab 文档) 关键点:它只托管“静态网站”,不跑后端(不跑 .NET 服务、不跑数据库)。适合文档站、前端构建产物、产品手册、项目主页等。 2. GitLab Pages 怎么工作的(核心机制) Pages 的发布有一个非常清晰的约定: CI 中有一个 Pages job(job 名可以叫 pages,也可以自定义)。 这个 job 必须产出一个目录(传统默认叫 public)作为 artifacts。 GitLab 会把这个 artifacts 部署成网站。(GitLab 文档) 早期常见约定是目录名必须叫 public。(GitLab 文档) 较新的方式是:你可以在 .gitlab-ci.yml 里用 pages: publish: 指定发布目录,比如 dist。(GitLab 文档) 3. 你最终会得到的 URL 长什么样 GitLab Pages 的域名/路径规则跟 GitLab 的用户/组/项目结构相关,常见会区分: Project Pages(项目级网站) Group Pages / User Pages(组/用户级网站) 支持自定义域名(GitLab 文档) 你是自建 GitLab,所以最关键不是“规则长啥样”,而是你们管理员在 GitLab 上给 Pages 配了什么域名(通常需要一个单独的 Pages 域名,比如 pages.xxx.com 或 pages.xxx.net,并做 DNS/证书配置)。 4. 自建 GitLab 上,Pages 要能用,管理员侧需要什么 这部分通常由运维/管理员做一次,之后所有项目都能用 Pages。 典型前置条件包括: 为 Pages 准备一个独立域名(例如 pages.example.com 或 example.io) 为该域名配置 通配符 DNS(例如 *.pages.example.com)(GitLab 文档) (可选但强烈建议)配置 通配符 TLS 证书,以便 *.pages... 全部走 HTTPS(GitLab 文档) 自建安装还需要 zip/unzip 等用于处理 artifacts 的依赖(具体取决于部署方式)(GitLab 文档) 如果你在 GitLab UI 里看不到 Pages 相关开关/设置,一般就是管理员还没启用或没配置。(GitLab 文档) 5. 最小可用的 Pages 示例(用 MkDocs 举例) 你不需要理解太多,掌握一条:CI 构建 → 产出静态目录 → Pages 发布 以 MkDocs 为例: stages: [build, deploy] build-docs: stage: build image: python:3.12-alpine script: - pip install mkdocs-material - mkdocs build -d public artifacts: paths: - public expire_in: 7 days pages: stage: deploy script: - echo "deploy pages" artifacts: paths: - public rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 说明: mkdocs build -d public:把站点生成到 public/ pages job 把 public/ 作为 artifact 给 GitLab Pages 用(GitLab 文档) 你会发现:Pages 的本质就是“把某个目录托管成网站”。 6. Pages 的权限与访问控制(你这种“内部知识库”很关键) GitLab Pages 支持访问控制:可以根据项目可见性/设置决定谁能访问 Pages。(GitLab 文档) 对你们团队知识库,我通常建议两种策略: 内部可见(需要 GitLab 登录):安全、适合内网知识 公开可见:适合对外文档(比如开源项目) 你想做“集中网站给团队看”,大概率选第 1 种。 7. 回到你的痛点:为什么 GitLab Pages 适合“集中式文档站” 你担心的不是“能不能发布”,而是: 500 仓库文档散落,没人看 需要一个统一入口 + 统一导航 + 统一搜索 还要能通知到 NodeBB/企业微信 Pages 在这里的价值是: 发布非常标准化:CI 产出静态目录即可(GitLab 文档) 权限可继承 GitLab 的组织结构(内部站点很方便)(GitLab 文档) 你可以把中央聚合仓库的 Pages 当作唯一入口(docs portal) 8. 你问“为什么不直接 NodeBB 发帖就算了”:Pages + NodeBB 的正确分工 结论:NodeBB 负责“通知与讨论”,Pages 负责“可长期引用的最终文档”。 Pages 站点:目录、搜索、稳定 URL、版本演进 NodeBB:把“本次变更摘要 + 链接”推送给所有人,并在贴内讨论 这两者叠加,你会得到: “大家会看到”(通知推送) “大家能查到”(文档站可检索) “半年后仍然可追溯”(Git + Pages 稳定链接) 9. 你下一步应该怎么验证 Pages 是否已就绪(不需要猜) 在你们自建 GitLab 上,最快的验证方式是: 新建一个测试项目 pages-test 放一个最简单的 .gitlab-ci.yml:创建 public/index.html 并发布 跑 pipeline,看项目的 Pages 设置里是否出现可访问 URL(或提示管理员启用) 官方也提供“从零创建 Pages”的指导思路(本质就是 CI 产出静态目录)。(GitLab 文档) 我建议你做的最短落地路径(针对你“集中站点 + 通知”) 先把 Pages 跑通:pages-test 项目发布一个静态页 建立 docs-portal(中央仓库)用 MkDocs 发布 Pages 让 docs-portal 作为唯一入口(先不急着聚合 500 项目) docs-portal 发布后:CI 同步推送到企业微信 + NodeBB(每日汇总更合理) 如果你愿意,我下一条可以直接给你一套“验证 Pages 就绪”的最小项目内容(index.html + .gitlab-ci.yml),以及一个 docs-portal 的 MkDocs 初始仓库骨架(导航、首页、日报/ADR 模板、发布后通知脚本接口),你复制进去就能跑。你只需要告诉我你们 GitLab 的 Pages 域名大概是怎样配置的(比如 *.pages.xxx.com 还是 pages.xxx.com/<group>/<project>),我就能把链接规则也写进模板里。