V2EX 热门帖子
1. 老黄给的免费 LLM API 用来做网页翻译用着很爽
前几天折腾了一下,ministral-14b 配合魔改之后的某著名闭源翻译插件,效果极佳,速度也飞快
最爽的是卡都不用绑,唯一的限制是每分钟只能请求 40 次,对翻译场景绰绰有余
作者: lemoncoconut | 发布时间: 2026-01-11 08:13
2. 想买个最便宜的云服务器做内网端口转发
想在国内买个最便宜的云服务器或者云容器,目的只有一个就是为家里的机器做端口转发,方便我在外边随时连接。 现在最便宜的似乎是阿里云和京东云,60 多一年,想知道各位大佬有没有什么更便宜的渠道? 我看云容器比云服务器还贵,真是没道理啊。
作者: timewarp | 发布时间: 2026-01-11 07:47
3. jetbrains 全家桶切换到 vscode 系列,快捷键大家怎么记的?
之前一直使用 jetbrains 全家桶,最近一直在使用 antigravity 进行开发。如果把快捷键设置成了 idea 快捷键怕影响 vscode 系 ide 特有的功能。最近这两年应该有很多人都切换了 ide ,想问下大家怎么处理的?常用哪些快捷键?
作者: GuluMashimaro | 发布时间: 2026-01-11 15:18
4. 2025 年里几乎没写过几行代码
一方面是公司业务萎缩,没活干,只剩下日常维护了,要修改的代码就仍给 ai 帮忙改,几乎没有自己写的
另一方面就是感觉写代码已经没啥意义了,费了几个小时劲写的代码质量还不如 Claude 几分钟内生成的好,vibe coding 的最大受益者倒成了产品经理,时不时就看到领导在朋友圈发自己的 vibecoding 项目,乐在其中。
感觉以前想要热衷于造轮子的想法蠢毙了,所幸也没怎么付诸于行动,现在瞧着 ai coding 的感觉像极了马车夫瞧见汽车的模样
作者: voidmnwzp | 发布时间: 2026-01-11 18:25
5. 基于 Pydantic-Resolve 和 FastAPI-Voyager 的 Clean Architecture 实践 – 一套面向复杂业务场景的 Python Web 开发方法论
基于 Pydantic-Resolve 和 FastAPI-Voyager 的 Clean Architecture 实践
篇幅较长无法粘贴全文,原文链接: https://github.com/allmonday/A-Python-web-development-methodology-for-complex-business-scenarios/blob/main/README.zh.md
一套面向复杂业务场景的 Python Web 开发方法论
目录
- 基于 Pydantic-Resolve 和 FastAPI-Voyager 的 Clean Architecture 实践
- 目录
- 1. 背景与问题
- 1.1 当前主流做法及其痛点
- [模式一:直接使用 ORM (如 SQLAlchemy )](#模式一直接使用-orm 如-sqlalchemy)
- 模式二:使用 ORM 的 Eager Loading
- 模式三:手动组装数据
- 模式四:使用 GraphQL
- 1.2 问题根源分析
- [问题 1:业务模型与数据模型混淆](#问题-1 业务模型与数据模型混淆)
- [问题 2:依赖方向错误](#问题-2 依赖方向错误)
- [问题 3:缺少业务关系的显式声明](#问题-3 缺少业务关系的显式声明)
- [问题 4:中间表的技术暴露](#问题-4 中间表的技术暴露)
- 2. Clean Architecture 思想
- 2.1 核心原则
- [原则 1:依赖规则](#原则-1 依赖规则)
- [原则 2:业务规则独立](#原则-2 业务规则独立)
- [原则 3:跨边界的数据传递](#原则-3 跨边界的数据传递)
- 2.2 依赖规则
- 2.3 在 Web 开发中的应用
- [3. Pydantic-Resolve:业务模型层](#3-pydantic-resolve 业务模型层)
- 3.1 核心概念
- [3.2 ERD:业务关系的声明](#32-erd 业务关系的声明)
- [3.3 DataLoader:批量加载的秘密](#33-dataloader 批量加载的秘密)
- [问题:N+1 查询](#问题 n1-查询)
- [解决方案:DataLoader](#解决方案 dataloader)
- [DefineSubset:字段选择与复用](#definesubset 字段选择与复用)
- [3.4 Resolve 与 Post:数据组装与计算](#34-resolve-与-post 数据组装与计算)
- [Resolve:声明数据依赖](#resolve 声明数据依赖)
- [Post:数据后处理](#post 数据后处理)
- 3.5 跨层数据传递
- [Expose:父节点向子节点暴露数据](#expose 父节点向子节点暴露数据)
- [Collect:子节点向父节点收集数据](#collect 子节点向父节点收集数据)
- 3.6 小结
- [4. FastAPI-Voyager:架构可视化](#4-fastapi-voyager 架构可视化)
- 4.0 为什么需要架构可视化?
- 4.1 核心功能
- 4.2 ERD 与 API Route 的结合
- 4.3 实战应用场景
- [场景 1:发现架构偏离](#场景-1 发现架构偏离)
- [场景 2:发现过度嵌套](#场景-2 发现过度嵌套)
- [场景 3:新人快速理解系统](#场景-3 新人快速理解系统)
- 5. 完整的开发流程
- 5.1 架构设计阶段
- [步骤 1:识别核心业务实体](#步骤-1 识别核心业务实体)
- [步骤 2:定义实体关系](#步骤-2 定义实体关系)
- 5.2 实体定义阶段
- 5.3 数据层实现
- [定义 ORM Models (以 ERD 为指导)](#定义-orm-models 以-erd-为指导)
- 实现 Loaders
- 5.4 API 实现阶段
- 5.5 可视化验证
- 6. 与其他方案的对比
- 7. 总结
- 参考资料
1. 背景与问题
1.1 当前主流做法及其痛点
在 Python Web 开发中,处理复杂业务场景时,开发者通常采用以下几种模式:
模式一:直接使用 ORM (如 SQLAlchemy )
@router.get("/teams/{team_id}", response_model=TeamDetail) async def get_team(team_id: int, session: AsyncSession = Depends(get_session)): # 获取团队基本信息 team = await session.get(Team, team_id) # 获取 Sprint 列表 sprints = await session.execute( select(Sprint).where(Sprint.team_id == team_id) ) team.sprints = sprints.scalars().all() # 获取每个 Sprint 的 Story for sprint in team.sprints: stories = await session.execute( select(Story).where(Story.sprint_id == sprint.id) ) sprint.stories = stories.scalars().all() # 获取每个 Story 的 Task for story in sprint.stories: tasks = await session.execute( select(Task).where(Task.story_id == story.id) ) story.tasks = tasks.scalars().all() # 获取每个 Task 的负责人 for task in story.tasks: task.owner = await session.get(User, task.owner_id) return team这种做法在简单场景下确实很直观,能够快速上手。ORM 的类型安全特性也能在编译时发现一些错误,而且与数据库表结构的一一对应关系让代码容易理解。但当我们面对真正的业务场景时,这种方式的缺陷很快就暴露出来了。
最致命的问题是 N+1 查询。虽然代码看起来很清晰,但执行时会产生大量的数据库查询。每当我们访问一个关联关系时,ORM 就会发起一次新的查询。在深层嵌套的情况下,查询数量会呈指数级增长。更糟糕的是,这种性能问题在开发阶段不容易发现,只有当数据量积累到一定程度后才会显现出来,那时候往往已经太晚了。
代码的组织方式也是个问题。数据获取的逻辑散落在各个嵌套的循环中,业务逻辑和数据获取逻辑混在一起,难以阅读和维护。当需要修改业务规则时,开发者不得不在复杂的嵌套结构中寻找修改点,很容易引入新的 bug 。性能更是不可控,随着数据量的增长,查询效率会急剧下降,而这些性能瓶颈很难在代码层面直接观察到。
此外,相似的数据获取逻辑会在多个 API 中重复出现,导致大量代码冗余。当一个 API 需要获取”团队及其 Sprint”,另一个 API 需要”团队及其成员”时,即使它们的查询逻辑非常相似,也不得不重复编写。这违反了 DRY ( Don’t Repeat Yourself )原则,增加了维护成本。
模式二:使用 ORM 的 Eager Loading
@router.get("/teams/{team_id}", response_model=TeamDetail) async def get_team(team_id: int, session: AsyncSession = Depends(get_session)): # 使用 joinedload 预加载关联数据 result = await session.execute( select(Team) .options( joinedload(Team.sprints) .joinedload(Sprint.stories) .joinedload(Story.tasks) .joinedload(Task.owner) ) .where(Team.id == team_id) ) return result.scalar_one()为了解决 N+1 查询问题,ORM 提供了 Eager Loading 机制,让我们可以通过
joinedload、selectinload等方式预先加载关联数据。代码变得更简洁了,性能问题也得到了缓解。但这种方案也带来了新的挑战。最明显的问题是笛卡尔积。当我们使用多层 JOIN 预加载关联数据时,数据库返回的数据量会急剧膨胀。比如一个团队有 10 个 Sprint ,每个 Sprint 有 10 个 Story ,每个 Story 有 10 个 Task ,那么 JOIN 的结果集会包含 1000 行数据,即使每行的数据量不大,也会给网络传输和内存占用带来压力。
更严重的问题是灵活性差。Eager Loading 的策略是在代码中硬编码的,所有使用同一个 Model 的 API 都会执行相同的预加载逻辑。但不同的 API 往往需要不同的数据。比如一个 API 只需要团队的基本信息,另一个 API 需要团队的 Sprint ,还有一个 API 需要团队的成员。如果统一使用 Eager Loading 加载所有关联数据,就会出现过度获取的问题,前端不需要的数据也被查询和传输了,浪费了资源。
配置 Eager Loading 本身就很复杂。开发者需要理解
lazy、joinedload、selectinload、subquery等多种加载策略的区别,知道什么时候用哪一种,以及它们各自会有什么副作用。这种配置错误很容易导致性能问题或意外的数据加载行为。而且,这种”一刀切”的配置方式意味着所有 API 都使用相同的加载策略,无法针对特定场景进行优化。模式三:手动组装数据
@router.get("/teams/{team_id}", response_model=TeamDetail) async def get_team(team_id: int, session: AsyncSession = Depends(get_session)): # 1. 批量获取所有需要的数据 team = await session.get(Team, team_id) sprints_result = await session.execute( select(Sprint).where(Sprint.team_id == team_id) ) sprint_ids = [s.id for s in sprints_result.scalars().all()] stories_result = await session.execute( select(Story).where(Story.sprint_id.in_(sprint_ids)) ) story_ids = [s.id for s in stories_result.scalars().all()] tasks_result = await session.execute( select(Task).where(Story.id.in_(story_ids)) ) tasks = tasks_result.scalars().all() owner_ids = list(set(t.owner_id for t in tasks)) owners_result = await session.execute( select(User).where(User.id.in_(owner_ids)) ) owners = {u.id: u for u in owners_result.scalars().all()} # 2. 手动组装数据结构 sprint_dict = {s.id: s for s in sprints_result.scalars().all()} story_dict = {s.id: s for s in stories_result.scalars().all()} for story in story_dict.values(): story.tasks = [t for t in tasks if t.story_id == story.id] for task in story.tasks: task.owner = owners.get(task.owner_id) for sprint in sprint_dict.values(): sprint.stories = [s for s in story_dict.values() if s.sprint_id == sprint.id] team.sprints = list(sprint_dict.values()) return team为了获得最优的性能和精确的数据控制,有经验的开发者会选择手动组装数据。这种方式完全掌控查询逻辑,可以精确控制每个查询的 SQL 语句,避免不必要的数据库访问。通过批量查询和智能的数据组装,可以获得最佳的性能,而且没有冗余数据。
但这种方式的代价是代码变得非常冗长。如上面的例子所示,为了获取一个团队的完整信息,我们需要编写多个查询,手动构建数据字典,然后通过嵌套循环组装数据。代码的长度和复杂度都大幅增加,而真正表达业务逻辑的代码反而被淹没在数据组装的细节中。
更容易出错也是个大问题。手动组装数据涉及到大量的索引操作和循环嵌套,很容易出现索引错误、空指针引用等 bug 。而且这些错误往往只有在运行时、特定数据条件下才会暴露,难以在开发阶段发现。
维护成本更是高昂。当业务规则发生变化时(比如需要添加一个新的关联关系),开发者需要在所有相关的 API 中修改数据组装逻辑。如果遗漏了某个地方,就会导致数据不一致。而且,相似的数据组装逻辑会在多个 API 中重复出现,违反了 DRY 原则。
最根本的问题是,这种代码已经变成了纯粹的数据搬运工,看不出任何业务意图。代码中充满了字典操作、循环嵌套、索引查找,而这些都是技术细节,与业务需求毫无关系。新加入的团队成员很难从这些代码中理解业务逻辑,业务知识的传递变得异常困难。
模式四:使用 GraphQL
type Query { team(id: ID!): Team } type Team { id: ID! name: String! sprints: [Sprint!]! } type Sprint { id: ID! name: String! stories: [Story!]! } type Story { id: ID! name: String! tasks: [Task!]! } type Task { id: ID! name: String! owner: User! }GraphQL 确实是一个很有吸引力的方案。前端可以按需获取数据,需要什么字段就查什么字段,不会有过度获取的问题。它提供了类型安全的查询接口,而且通过 DataLoader 可以自动解决 N+1 查询问题。这些特性让 GraphQL 在前端开发中广受欢迎。
但 GraphQL 的学习曲线非常陡峭。开发者需要学习全新的查询语言、Schema 定义、Resolver 编写、DataLoader 配置等一堆概念,这与 REST API 的直观性形成了鲜明对比。更麻烦的是,GraphQL 的过度灵活性给后端带来了巨大的挑战。前端可以构造任意复杂的查询,有些查询甚至可能是开发者没有想到过的,这导致后端很难进行针对性的优化。当一个查询嵌套了 10 层,返回了数百万条数据时,数据库和服务器都会面临巨大的压力。
调试 GraphQL API 也比调试 REST API 复杂得多。当一个 GraphQL 查询出错时,错误信息往往很难定位到具体的问题源头。而且 GraphQL 需要额外的服务器和工具链支持,无法直接利用现有的 FastAPI 生态系统。比如 FastAPI 的依赖注入、中间件、自动文档生成等特性,在 GraphQL 中都无法直接使用。
还有一个更深层次的问题是 ERD 和用例的界限模糊。GraphQL 的 Schema 同时扮演了实体模型和查询接口两个角色。当我们设计一个 GraphQL Schema 时,很难确定应该按照实体来组织(一个 Type 对应一个数据库表),还是按照用例来组织(不同的业务场景需要不同的字段)。这导致最佳实践不清晰,不同的项目、不同的开发者可能有完全不同的组织方式。
而且随着业务增长,所有的用例都会堆砌在同一个 Schema 中,导致 Schema 膨胀,难以维护。权限控制也变得异常复杂。不同的 API 端点可能有不同的权限要求,但它们可能都查询同一个实体(比如 User ),在 GraphQL 中很难针对不同的查询场景应用不同的权限规则。
1.2 问题根源分析
上面我们探讨的所有模式,虽然表面上的问题各不相同,但它们的核心困境其实是一致的。
问题 1:业务模型与数据模型混淆
# SQLAlchemy ORM 同时扮演两个角色: # 1. 数据模型(如何存储) # 2. 业务模型(业务概念) class Team(Base): __tablename__ = 'teams' id = Column(Integer, primary_key=True) name = Column(String) # 这是数据库的外键关系,还是业务关系? sprints = relationship("Sprint", back_populates="team")在传统的 ORM 开发中,业务模型和数据模型是混在一起的。看看这个例子,
Team类既表达了业务概念(团队是什么),又承载了数据模型的细节(如何在数据库中存储)。当我们在sprints字段上定义relationship时,这到底是在描述一个业务关系(团队有多个 Sprint ),还是在声明一个数据库外键约束?这种模糊性会导致很多问题。数据库的设计约束会直接影响我们的业务建模。比如,如果数据库中的
teams表没有直接到users的外键,而是通过中间表team_members关联,那么在 ORM 中我们也必须通过这个中间表来定义关系。这意味着业务模型被迫适应数据库的实现细节,而不是反过来。更严重的是,这种方式无法表达跨库、跨服务的业务关系。现代系统中,数据可能分布在不同的数据库中,甚至存储在外部服务里。比如用户的基本信息在 PostgreSQL ,而用户的偏好设置在 MongoDB ,用户的实时状态在 Redis 中。ORM 的
relationship无法跨越这些边界,业务模型因此被限制在了单一数据库的范围内。问题 2:依赖方向错误
传统架构的依赖方向: ┌─────────────┐ │ API Layer │ ← 依赖于 └──────┬──────┘ │ ↓ ┌─────────────┐ │ ORM Models │ ← 依赖于 └──────┬──────┘ │ ↓ ┌─────────────┐ │ Database │ └─────────────┘ 问题:业务规则依赖于数据库实现!这违反了 Clean Architecture 的依赖规则。正确的依赖关系应该是:业务规则最稳定,不依赖任何外层;数据库是实现细节,应该依赖业务规则;当数据库变化时,业务规则不应该受影响。但传统架构的依赖方向恰恰相反,业务规则被数据库的实现细节所绑架。
问题 3:缺少业务关系的显式声明
# 传统方式:业务关系隐藏在查询中 async def get_team_tasks(team_id: int): # "团队的任务"这个业务概念隐藏在 SQL WHERE 中 result = await session.execute( select(Task) .join(Sprint, Sprint.id == Task.sprint_id) .where(Sprint.team_id == team_id) ) return result.scalars().all()业务关系没有被显式声明出来,这是个很隐蔽但危害很大的问题。看看这个例子,”团队的任务”是一个清晰的业务概念,但这个概念被隐藏在 SQL 的 JOIN 和 WHERE 子句中。新加入团队的成员需要阅读大量代码才能理解系统中有哪些业务关系,这些关系是如何定义的。更糟糕的是,没有自动化的方式来检查业务关系的一致性。当需求变化需要修改某个关系时,开发者很难找到所有相关的代码,很容易遗漏某个地方,导致业务逻辑的不一致。
问题 4:中间表的技术暴露
在 SQLAlchemy ORM 中,多对多关系需要显式定义中间表,这导致技术细节泄漏到业务层。
# SQLAlchemy ORM:必须定义中间表 class Team(Base): __tablename__ = 'teams' id = Column(Integer, primary_key=True) name = Column(String) # ORM relationship 需要指定中间表 members = relationship("User", secondary="team_members", # 必须指定中间表 back_populates="teams") class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) teams = relationship("Team", secondary="team_members", # 必须指定中间表 back_populates="members") # 中间表(技术实现细节) class TeamMember(Base): __tablename__ = 'team_members' team_id = Column(Integer, ForeignKey('teams.id'), primary_key=True) user_id = Column(Integer, ForeignKey('users.id'), primary_key=True) role = Column(String) # 可能还有额外字段 # 查询时需要关心中间表的存在 @router.get("/teams/{team_id}") async def get_team_members(team_id: int, session: AsyncSession): # 必须通过中间表查询 result = await session.execute( select(User) .join(TeamMember, TeamMember.user_id == User.id) # 中间表暴露 .where(TeamMember.team_id == team_id) ) return result.scalars().all()这个问题的根源在于,ORM 的多对多关系需要显式定义中间表,这导致技术细节直接泄漏到业务层代码中。业务代码必须知道
team_members中间表的存在,查询时也需要显式地 join 这个中间表。这增加了代码复杂度,更重要的是,业务逻辑被数据库的实现细节所绑架。更深层的问题是业务语义变得模糊。
TeamMember到底是一个有意义的业务概念,还是纯粹的技术实现?如果中间表还有额外的字段(比如role表示用户在团队中的角色,joined_at表示加入时间),这些字段应该被建模为独立的实体吗?不同的开发者可能给出不同的答案,缺乏统一的指导原则。数据组装也因此变得复杂。查询”团队的所有成员”需要 join 中间表,查询”用户所属的团队”也需要 join 中间表。所有涉及多对多关系的查询都变得冗长和难以理解。当业务规则要求”获取用户在所有团队中的角色”时,情况就更加复杂了。这些技术细节让业务逻辑的实现变得异常沉重。
对比:Pydantic-Resolve ERD 的方式
# ERD:业务概念清晰,无需关心中间表 class TeamEntity(BaseModel, BaseEntity): """团队实体 - 业务概念""" __relationships__ = [ # 直接表达"团队有多个成员"的业务关系 Relationship( field='id', target_kls=list[UserEntity], loader=team_to_users_loader # loader 内部处理中间表 ), ] id: int name: str class UserEntity(BaseModel, BaseEntity): """用户实体 - 业务概念""" __relationships__ = [ # 直接表达"用户属于多个团队"的业务关系 Relationship( field='id', target_kls=list[TeamEntity], loader=user_to_teams_loader ), ] id: int name: str # Loader 实现细节:中间表只在这里出现 async def team_to_users_loader(team_ids: list[int]): """加载团队成员 - 内部处理中间表""" async with get_session() as session: # 只有这里需要知道中间表的存在 result = await session.execute( select(User) .join(TeamMember, TeamMember.user_id == User.id) .where(TeamMember.team_id.in_(team_ids)) ) users = result.scalars().all() # 构建映射 users_by_team = {} for user in users: for tm in user.team_memberships: if tm.team_id not in users_by_team: users_by_team[tm.team_id] = [] users_by_team[tm.team_id].append(user) return [users_by_team.get(tid, []) for tid in team_ids]关键差异 :
维度 SQLAlchemy ORM Pydantic-Resolve ERD 中间表位置 暴露在业务层 隐藏在 loader 实现中 业务语义 技术关系 ( secondary)业务关系 ( 团队包含成员)查询代码 需要 join 中间表 loader.load(team_id)代码位置 分散在多处 集中在 loader 测试 依赖数据库表结构 可 mock loader 架构优势 :
传统方式: Team → TeamMember (中间表) → User 业务层需要知道中间表的存在 Pydantic-Resolve 方式: Team → User (业务关系) 中间表是数据层的实现细节,业务层不关心这意味着:
业务模型纯净 :Team 和 User 的关系直接表达业务语义
技术细节封装 :中间表的存在被封装在 loader 中
灵活的存储策略 :
- 数据库可以用中间表实现
- 也可以用 JSON 字段存储
- 甚至可以是外部服务(如 LDAP )
- 业务层代码无需修改
易于理解 :新人看到 ERD 就能理解业务关系,不需要先学习数据库设计
2. Clean Architecture 思想
2.1 核心原则
Clean Architecture 由 Robert C. Martin (Uncle Bob) 提出,核心思想是:
“Software architecture is the art of drawing lines that I call boundaries.” 软件架构的艺术在于画界线。
原则 1:依赖规则
外层依赖内层,内层不依赖外层。 ↓ 依赖方向 ┌─────────────────────┐ │ Frameworks & │ 外层 │ Drivers │ (实现细节) ├─────────────────────┤ │ Interface │ │ Adapters │ ├─────────────────────┤ │ Use Cases │ │ (Application) │ ├─────────────────────┤ │ Entities │ 内层 │ (Business Rules) │ (核心) └─────────────────────┘遵循依赖规则有几个关键点需要注意。首先,内层不知道外层的存在,这意味着核心业务逻辑不依赖于任何框架、数据库或 UI 的细节。其次,内层不包含外层的信息,比如业务规则不应该知道数据是用 PostgreSQL 还是 MongoDB 存储的。最后,外层的实现可以随时替换而不影响内层,这意味着我们可以从 SQLAlchemy 切换到 MongoDB ,或者从 FastAPI 切换到 Django ,而业务逻辑代码无需修改。
原则 2:业务规则独立
# ❌ 错误:业务规则依赖数据库 class Task: def calculate_priority(self, session): # 业务逻辑被数据库实现细节污染 if self.assignee_id in session.query(TeamMember).filter_by(role='lead'): return 'high' # ✅ 正确:业务规则独立 class Task: def calculate_priority(self, assignee_roles): # 业务逻辑只依赖业务概念 if 'lead' in assignee_roles: return 'high'原则 3:跨边界的数据传递
# 内层定义数据结构 class TaskEntity(BaseModel): id: int name: str assignee_id: int # 外层负责转换 def task_entity_to_orm(entity: TaskEntity) -> Task: return Task( id=entity.id, name=entity.name, assignee_id=entity.assignee_id )2.2 依赖规则
在 Web 开发中,依赖规则可以这样理解:
┌────────────────────────────────────────────────────┐ │ Presentation Layer (外层) │ │ - FastAPI Routes │ │ - Request/Response Models │ │ - 依赖: Application Layer │ └────────────────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────────────────┐ │ Application Layer (Use Cases) │ │ - 业务用例(获取用户、创建订单) │ │ - 依赖: Domain Layer │ └────────────────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────────────────┐ │ Domain Layer (内层) │ │ - Entities (业务实体) │ │ - Business Rules (业务规则) │ │ - Value Objects (值对象) │ │ - 不依赖任何外层 │ └────────────────────────────────────────────────────┘ ↓ ┌────────────────────────────────────────────────────┐ │ Infrastructure Layer (最外层) │ │ - Database (SQLAlchemy) │ │ - External Services │ │ - File System │ └────────────────────────────────────────────────────┘关键洞察 :
- Entities 不应该知道 SQLAlchemy 的存在
- Business Rules 不应该知道数据库表结构
- Use Cases 不应该知道 HTTP 协议的细节
2.3 在 Web 开发中的应用
传统架构的问题
# 传统方式:所有层次耦合 # Domain Layer (应该独立,但实际上依赖了 ORM) class User(Base): # ← SQLAlchemy Base __tablename__ = 'users' id = Column(Integer, primary_key=True) # Application Layer (应该只依赖 Domain ,但直接使用了 ORM) async def create_user(data: dict, session: AsyncSession): user = User(**data) # ← 直接使用 ORM Model session.add(user) await session.commit() # Presentation Layer @router.post("/users") async def api_create_user(data: dict, session=Depends(get_session)): return await create_user(data, session) # ← 暴露了数据库细节这段代码暴露了传统架构的核心问题。SQLAlchemy 虽然建立了对象关系映射( ORM ),让数据库表可以通过 Python 对象来操作,但这种映射关系过于紧密。ORM Model 既承担了数据持久化的职责,又要表达业务概念,导致对象无法自由地代表业务模型。业务实体被数据库的实现细节所绑架,每个字段、每个关系都必须与数据库表结构一一对应,完全失去了作为独立业务概念存在的自由。
更深层次的问题包括:
- Domain Layer 被 SQLAlchemy 绑定 :业务实体继承了 SQLAlchemy 的 Base ,无法独立于数据库存在
- 业务逻辑无法脱离数据库测试 :编写单元测试时必须启动完整的数据库环境,大大降低了测试效率
- 切换数据库需要修改所有层 :当从 PostgreSQL 迁移到 MongoDB 时,所有使用 ORM Model 的代码都需要重写
。。。
作者: tangkikodo | 发布时间: 2026-01-11 10:26
6. 以后发软件库是不是只用发 spec?
X 上看到 阮一峰 发了一个好玩的库:
https://x.com/ruanyf/status/2010225187594334692
https://github.com/dbreunig/whenwords
顺手一个小时就 Vibe coding 了一个 Ruby Implementation
https://github.com/ZPVIP/whenwords
感觉以后可以用 spec 发库了
作者: zpvip | 发布时间: 2026-01-11 08:31
7. Gemini Code Assist 插件是如何用来做代码审查?
RT ,网上找了一圈,只有 gemini cli 来审查代码。
作者: Hyvi | 发布时间: 2026-01-11 15:33
8. 北京的 AGI-Next 闭门会,有人在现场么?谈论了点什么?
如题:
![]()
作者: ToDayMkCode | 发布时间: 2026-01-11 14:26
9. 以后验收是不是得控制代码行数了
比如一个小项目,乙方用 AI 直接拉了托几十 w 行的代码,但功能都没问题。
作者: COW | 发布时间: 2026-01-11 05:24
10. 甲骨文的免费 ARM 实例收费问题
Oracle 云升级了付费账号,创建了一个 4/24G 内存的 ARM 实例,过了几天修改实例把内存 24G 改成 25G ,Cost 报表里没产生费用明细,月底出账单发现依旧没有产生费用。
难道 A1 的 ARM 实例不是超出 4 核 24G 会收费的吗?
作者: none | 发布时间: 2026-01-11 09:51
11. 这把实锤了, win11 是微软 25 年来最慢的系统
https://www.techspot.com/news/110817-windows-11-performs-worse-than-older-windows-versions.html
测试所有指标都落后以前的老系统
https://www.eteknix.com/microsoft-may-have-created-the-slowest-windows-in-25-years-with-windows-11/
作者: iorilu | 发布时间: 2026-01-11 09:07
12. vercel 自动部署问题
我的 vercel 自动部署问题,现在解决了,但是怎么解决的,我也没弄清楚,先写出来,请大家帮忙分析一下。 我之前做了个网站放到 git ,想自动部署到 vercel ,但是怎么配置,怎么修改授权都不成功,就只能每次都删除旧项目,然后手动同步。有网友提醒,可能是提交到 git 的账号和 vercel 上授权的不是一个账号,我也检查修改了很多次,一直没成功。今天做了个新网站,https://dreamyroomlevel.org/ 是个游戏攻略网站,还是按之前的方法,用户名,授权都没改,先传到 git ,然后在同步到 vercel 部署,但是神奇的是,这次就可以自动部署,每次提交都可以自动部署了。不知道为什么,不知道哪位大佬也遇到过相同的问题。
作者: bingkunzhao | 发布时间: 2026-01-11 08:57
13. 卸载了 KB5072033 电脑快了不少
我卸载了 2025-12 安全更新(KB5072033) (26100.7462),windows 的锁屏界面也不卡了,用起来丝滑了很多。微软真的不干人事
作者: Aokichiharu | 发布时间: 2026-01-11 05:04
14. OpenCode + GLM-4.7 还是弟弟, Antigravity + Gemini 3 Pro 还是强不少,不写一行代码转译了一个 Gem: rb-edge-tts
原库是 Python 写的: https://github.com/rany2/edge-tts
我先用 OpenCode + GLM-4.7 转成 Ruby Gem. 提示词:
详细研读 python 代码,帮我把这个功能库转换成一个 Ruby gem, 并写出相应的 README.md, 比如 安装,调用等。
最后没成功,然后它在根目录留下几个 markdown 文件分析了一下它觉得的问题所在,然后就不管了。每次让它继续还是搞不定。没有买其它模型,说不定其它模型效果会好一点。
最后还是在 Antigravity + Gemini 3 Pro 下丝滑完工: https://github.com/ZPVIP/rb-edge-tts (欢迎试用,发 Issue ,PR)
全程我都没有写代码,这在以前根本无法想象。
作者: zpvip | 发布时间: 2026-01-10 18:40
15. 分享一个免费使用内网穿网站 tikrok.cc
新开了一个网站,tikrok.cc ,目前免费使用。 没什么浏览,去 google 搜索了一下,都搜不到。
作者: 5wunian | 发布时间: 2026-01-11 00:52
16. 如何设置代码智能预测
Claude Code, Codex, Open Code 可以像 cursor 那样,写代码的时候智能预测接下来要写的代码吗?一直用 Cursor 这个 Tab 补全功能,发现其他的都不支持,不知道是不是哪里没设置好。
作者: uruana | 发布时间: 2026-01-11 02:36
17. Incremark Solid 版本上线: Vue/React/Svelte/Solid 四大框架,统一体验
Incremark 现已支持 Solid ,至此完成了对 Vue 、React 、Svelte 、Solid 四大主流前端框架的全面覆盖。
为什么要做框架无关
市面上大多数 Markdown 渲染库都是针对特定框架开发的。React 生态有 react-markdown ,Vue 生态有各种 v-md 组件。这带来几个问题:
- 重复造轮子 :每个框架社区都在独立实现相似的功能
- 能力不一致 :不同框架的实现质量参差不齐
- 团队切换成本 :换框架意味着重新学习新的 API
Incremark 采用不同的思路:核心逻辑与 UI 框架完全解耦 。
@incremark/core负责所有解析、转换、增量更新的工作,输出的是框架无关的数据结构。各框架包(@incremark/vue、@incremark/react、@incremark/svelte、@incremark/solid)只需要把这些数据渲染成对应框架的组件即可。这意味着:
- 核心能力一次实现,四个框架同时受益
- Bug 修复和性能优化自动同步到所有框架
- API 设计保持高度一致,切换框架几乎零学习成本
包结构
┌───────────────────────────────┐ │ @incremark/core │ │ │ │ 增量解析 · 双引擎 · 插件系统 │ └───────────────┬───────────────┘ │ ▼ ┌───────────────────────────────┐ │ @incremark/vue │ │ @incremark/react │ │ @incremark/svelte │ │ @incremark/solid ← NEW │ └───────────────┬───────────────┘ │ ▼ ┌───────────────────────────────┐ │ @incremark/theme │ │ │ │ 样式 · 主题 · 代码高亮 │ └───────────────────────────────┘增量解析
传统 Markdown 渲染器在流式场景下存在性能问题:每次新内容到达都要重新解析整个文档,复杂度是 O(n²)。
Incremark 只处理新增内容,已解析的块不再重复处理,复杂度降至 O(n)。
四个框架的用法对比
四个框架的组件 API 完全一致,只是语法风格不同:
Vue
<script setup> import { IncremarkContent } from '@incremark/vue' // ... </script> <template> <IncremarkContent :content="content" :is-finished="isFinished" /> </template>React
import { IncremarkContent } from '@incremark/react' // ... <IncremarkContent content={content} isFinished={isFinished} />Svelte
<script> import { IncremarkContent } from '@incremark/svelte' // ... </script> <IncremarkContent content={content} isFinished={isFinished} />Solid
import { IncremarkContent } from '@incremark/solid' // ... <IncremarkContent content={content()} isFinished={isFinished()} />可以看到,除了各框架本身的响应式语法差异( Vue 的
ref、React 的useState、Svelte 的$state、Solid 的createSignal),组件的使用方式完全统一。在线演示
链接
- npm: @incremark/core
- 文档: incremark.com
- GitHub: github.com/anthropics/incremark
MIT 许可证。
作者: 1244943563 | 发布时间: 2026-01-10 19:12
18. 大模型 agents 为什么不自带 skills?
为什么大模型不自带 skills ?可能所有 skills token 太多了,那可以维护一个云端技能列表,模型按需加载。
作者: ethusdt | 发布时间: 2026-01-09 14:32
19. 各位大佬,店内人脸识别方案有推荐吗
记性太差,守店老会忘记再进店的老顾客;
各位大佬有推荐稳定的人脸识别的软/硬件不;
作者: tmdgri | 发布时间: 2026-01-10 12:07
20. 解决了一个非常棘手的问题,内心止不住的开心。
15 年前的项目,sqlserver2008r2
php 5.6 / 7 项目
开发环境 mac 26.2 的系统。
各种编译 pdo_sqlsrv 扩展终于解决了。可以顺畅开发了
主要故障源于 arm 架构无法编译 pdo_sqlsrv 低版本的扩展。
mac 的 odbc17 只支持 tls1.2 以上。 sqlserver2008r2 不支持 tls1.2
odbc13 已经不可用。这顿捣鼓
搞了两天两夜 可算搞定了。能睡个好觉
作者: gyinbj | 发布时间: 2026-01-10 18:49
21. 现在感觉产品以后交付都是按小时算时间了
AI 肯定是越来越强的
上次看文章说, claude 可以连续编程 5 个小时
那至少都是几万行代码起码
大多小产品其实可能都不到万行, 就算加上文档注释测试等, 有 1,2 万行不错了
那就是说以后产品真的按小时计算了, 甚至当天交付…
搞得和京东一样, 保证隔天收货了
作者: iorilu | 发布时间: 2026-01-10 09:47
22. 有没有按照 Fluent Design 设计的 Clash 推荐呢?
隔壁 mac 都有 clashMac 了,看着挺好看的,现在用的 clash verge 界面和 Windows 不太搭配,有点丑😇 以前找到过一个: https://github.com/Daydreamer-riri/clash-verge-rev-fluent ,但是现在太久没更新了😇
作者: JordanQD | 发布时间: 2026-01-10 09:21
23. antigravity generate commit message 功能突然无法使用了(Beta)
antigravity 刚开始 generate commit message 功能,正常使用。
最近发现,在 antigravity 上点击 generate commit message 功能,会弹出错误,无法生成 commit 信息。
有人遇到类似问题吗?
PS: 其他的功能看起来都是正常的。
2026-01-09 22:34:15.736 [info] E0109 22:34:15.681222 30855 log.go:354] error converting token count to int: unleashWrapper is nil 2026-01-09 22:34:21.338 [info] E0109 22:34:21.338463 30855 log.go:362] Failed to generate commit message: error grabbing LLM response: stream error 2026-01-09 22:34:21.339 [info] E0109 22:34:21.339636 30855 interceptor.go:74] /exa.language_server_pb.LanguageServerService/GenerateCommitMessage (unknown): error grabbing LLM response: stream error环境:
MacBook Pro (Retina, 15-inch, Mid 2015) 12.7.4 (21H1123)
版本: Antigravity 提交: 1.13.3 日期: 1.104.0 Electron: 94f91bc110994badc7c086033db813077a5226af ElectronBuildId: 2025-12-19T21:03:14.401Z Chromium: 37.3.1 Node.js: 138.0.7204.235 V8: 22.18.0 OS: 13.8.258.31-electron.0
作者: sn0wdr1am | 发布时间: 2026-01-09 14:37
24. 系统自带的 IPSEC/IKEv2 客户端,自动重连问题
最近在写 IPSEC/IKEv2 的服务端,目前已经能使用系统(windows10/macos/ios/android)自带的 VPN 客户端连接上来了。
实际使用的时候,发现个问题,就是不管是 windows 系统还是 macos 系统,只要系统断网了(比如 wifi 断了),然后 VPN 也会自动断掉,断掉之后 VPN 不会自己重新连接,需要手动重新连接。手机端的话,感觉手动重连还行,电脑端的话,如果电脑不在旁边就就没法连上 VPN 了。
不知道大家实际使用系统自带的 VPN 客户端时,是咋解决这个问题的?
作者: raltrk | 发布时间: 2026-01-10 14:23
25. iPad 上有类似 lanuchos 的类似免费软件吗?
这里推荐了一轮, 但不知道哪个会更好用, 更容易上手。
作者: Hermitist | 发布时间: 2026-01-10 15:30
26. 解决老账号 Google One 提示 “此账号无法订阅 Google One AI Pro” 解决方法
提示下
antigravity增加周限额了,专门为 antigravity 而半价年付需要谨慎。之前我一直在用老账号老提示“此账号无法订阅 Google One AI Pro”,变更过地址,一直不行,搁置许久。今天心血来潮又折腾了一下,终于搞定了。分享下处理步骤:
- 访问以下网址 https://policies.google.com/terms ,登录自己的账号即可查看账号当前的国家或地区,中国的话,请换区(注意,申请换区换区理由选“其他”;一年只能修改一次) —– 换成功了,没有效果
- 看下“付款资料” https://pay.google.com/gp/w/home/settings ,看下付款地区是否有中国的,有的话就删除(底部有“关闭支付资料”按钮,可以删除当前默认的)。增加一个其他支持的国家的。
- 如果还是不行,看下是否开通过 google enterprise 的试用,如果有,请关闭 (之前有一个月的免费使用活动的羊毛,gemini 告诉我需要关闭)
完事儿,可以再重新进入 https://one.google.com/about/plans 页面试试看出现没出现半价包年的优惠。
作者: tyrad | 发布时间: 2026-01-09 15:19
27. 大模型写的代码几乎一摸一样
提了一个项目需求,让 chatgpt 分析,把分析的结果给 glm 4.7 和 claude code 4.5 ,发现两者写的代码几乎一摸一样。。。。。
作者: guotie | 发布时间: 2026-01-09 08:53
28. 寻找个仿 chrome/edge 的收藏栏的个人首页,自己找 Gemini 写了半天终觉差了点什么
由于个人的某种原因,经常要更换电脑工作,浏览器同步收藏同步就成了个问题,github 找了找大多都是像手机那样的一格子、一格子布局于是找 Gemini 花了半天高了个,但是总感觉差了点
大多都是 AI 写的
因为我基础只是懂点皮毛的那种
目前只能凑合着用![]()
作者: XINHL | 发布时间: 2026-01-10 03:45
29. vibe coding 了下我的 NAT 网络地址转换工具,新增了管理页面和端口段 Redirect 支持
NFTables NAT Rust
基于 nftables 的高性能 NAT 端口转发管理工具,使用 Rust 语言开发。
✨ 核心特性
- 🔄 动态 NAT 转发 :自动监测配置文件和目标域名 IP 变化,实时更新转发规则
- 🌐 IPv4/IPv6 双栈支持 :完整支持 IPv4 和 IPv6 NAT 转发
- 📝 灵活配置 :支持传统配置文件和 TOML 格式,满足不同使用场景
- 🎯 精准控制 :支持单端口、端口段、TCP/UDP 协议选择
- 🔌 本地重定向 :支持端口重定向到本机其他端口
- 🐋 Docker 兼容 :与 Docker 网络完美兼容
- ⚡ 高性能轻量 :基于 Rust 编写,仅依赖标准库和少量核心库
- 🚀 开机自启 :支持 systemd 服务管理,开机自动启动
- 🔍 域名解析 :支持域名和 IP 地址,自动 DNS 解析和缓存
- 🖥️ Web 管理界面 :提供可视化的 WebUI 管理配置和查看规则
![]()
🖥️ 系统要求
适用于以下 Linux 发行版:
- CentOS 8+ / RHEL 8+ / Fedora
- Debian 10+ / Ubuntu 18.04+
- 其他支持 nftables 的现代 Linux 发行版
⚙️ 系统准备
CentOS / RHEL / Fedora
# 关闭 firewalld systemctl disable --now firewalld # 关闭 SELinux setenforce 0 sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config # 安装 nftables yum install -y nftablesDebian / Ubuntu
# 安装 nftables apt update && apt install -y nftables # 禁用 iptables (可选) systemctl disable --now iptables📦 快速安装
升级也使用相同的安装命令
方法一:TOML 配置文件版本(推荐)
bash <(curl -sSLf https://us.arloor.dev/https://github.com/arloor/nftables-nat-rust/releases/download/v2.0.0/setup.sh) toml方法二:传统配置文件版本
bash <(curl -sSLf https://us.arloor.dev/https://github.com/arloor/nftables-nat-rust/releases/download/v2.0.0/setup.sh) legacy🆕 WebUI 管理界面
本项目现已支持 Web 管理界面,可以通过浏览器方便地管理 NAT 配置。
- 🔐 基于 JWT 的安全认证
- 🔒 支持 HTTPS/TLS 加密传输
- 📝 可视化编辑配置文件(支持传统格式和 TOML 格式)
- 📋 实时查看 nftables 规则
- 🎨 现代化的用户界面
安装管理界面 WebUI
bash <(curl -sSLf https://us.arloor.dev/https://github.com/arloor/nftables-nat-rust/releases/download/v2.0.0/setup-console.sh) # -p 5533 -k /root/.acme.sh/arloor.dev/arloor.dev.key -c /root/.acme.sh/arloor.dev/fullchain.cer
- 安装过程会交互式提示输入用户名和密码。密码会保存在 systemd 文件中,注意安全。
- 通过
-p参数可以指定 WebUI 监听端口,默认端口为 5533 。- 通过
-c和-k参数可以指定自定义 TLS 证书和私钥文件路径,如果未提供,将自动生成自签名证书。- 安装脚本会自动检测现有 NAT 服务的配置格式,并根据配置格式生成相应的 systemd service 文件。
安装完成后,访问
https://your-server-ip:5533即可使用管理界面。详细文档请查看 nat-console/README.md升级 WebUI
bash <(curl -sSLf https://us.arloor.dev/https://github.com/arloor/nftables-nat-rust/releases/download/v2.0.0/setup-console-assets.sh) systemctl restart nat-console📝 配置说明
TOML 配置文件(推荐)
配置文件位置:
/etc/nat.toml优势 :
✅ 支持配置验证,保证格式正确
✅ 支持注释,便于维护
✅ WebUI 可视化编辑和验证
✅ 结构化配置,可读性更好
============ 基础转发示例 ============
1. 单端口转发 - HTTPS 流量转发
[[rules]]
type = “single”
sport = 10443 # 本机监听端口
dport = 443 # 目标服务端口
domain = “example.com” # 目标域名或 IP 地址
protocol = “all” # 协议: all, tcp 或 udp
ip_version = “ipv4” # IP 版本: ipv4, ipv6 或 all
comment = “转发 HTTPS 到 example.com”2. 端口段转发 - 批量游戏端口
[[rules]]
type = “range”
port_start = 20000 # 起始端口
port_end = 20100 # 结束端口(含)
domain = “game.example.com”
protocol = “tcp” # 仅 TCP 协议
ip_version = “all” # 同时支持 IPv4 和 IPv6
comment = “游戏服务器端口段”3. UDP 专用转发 - DNS 服务
[[rules]]
type = “single”
sport = 5353 # 本机 DNS 端口
dport = 53 # 目标 DNS 端口
domain = “8.8.8.8” # 也可以直接使用 IP 地址
protocol = “udp” # 仅 UDP 协议
ip_version = “ipv4”
comment = “DNS 查询转发”============ 本地重定向示例 ============
4. 单端口重定向到本机服务
[[rules]]
type = “redirect”
sport = 8080 # 外部访问端口
dport = 3128 # 本机实际服务端口
protocol = “all”
ip_version = “ipv4”
comment = “代理服务端口重定向”5. 端口段重定向到本机
[[rules]]
type = “redirect”
sport = 30001 # 起始端口
sport_end = 30100 # 结束端口
dport = 45678 # 本机目标端口
protocol = “tcp”
ip_version = “all”
comment = “批量端口重定向到本机”============ 高级场景示例 ============
6. 强制 IPv6 转发
[[rules]]
type = “single”
sport = 9001
dport = 9090
domain = “ipv6.example.com”
protocol = “all”
ip_version = “ipv6” # 仅使用 IPv6 进行转发
comment = “IPv6 专用服务”7. 双栈支持示例 - 自动选择 IPv4/IPv6
[[rules]]
type = “single”
sport = 10080
dport = 80
domain = “dual-stack.example.com” # 域名同时有 A 和 AAAA 记录
protocol = “tcp”
ip_version = “all” # 根据客户端 IP 版本自动选择
comment = “双栈 Web 服务”传统配置文件
配置文件位置:
/etc/nat.conf基础格式 :
SINGLE,本机端口,目标端口,目标地址[,协议][,IP 版本]- 单端口转发RANGE,起始端口,结束端口,目标地址[,协议][,IP 版本]- 端口段转发REDIRECT,源端口,目标端口[,协议][,IP 版本]- 重定向到本机端口REDIRECT,起始端口-结束端口,目标端口[,协议][,IP 版本]- 端口段重定向参数说明 :
- 协议可选值:
tcp、udp、all(默认为all)- IP 版本可选值:
ipv4、ipv6、all(默认为all)- 以
#开头的行为注释配置示例 :
# ============ 基础转发 ============ # 单端口转发 - HTTPS 流量 SINGLE,10443,443,example.com # 端口段转发 - 游戏服务器端口( 20000-20100 ) RANGE,20000,20100,game.example.com # ============ 协议指定 ============ # 仅转发 TCP 流量 - Web 服务 SINGLE,10080,80,web.example.com,tcp # 仅转发 UDP 流量 - DNS 查询 SINGLE,5353,53,8.8.8.8,udp # ============ 本地重定向 ============ # 单端口重定向到本机服务 REDIRECT,8080,3128 # 端口段重定向到本机( 30001-30100 → 45678 ) REDIRECT,30001-30100,45678 # TCP 专用重定向 REDIRECT,7000-7100,8080,tcp # ============ IPv6 支持 ============ # 强制使用 IPv6 转发 SINGLE,9001,9090,ipv6.example.com,all,ipv6 # 双栈支持(根据客户端自动选择) SINGLE,10080,80,dual-stack.example.com,tcp,all # 禁用的规则(以 # 开头) # SINGLE,3000,3000,disabled.example.com🚀 使用方法
启动/停止服务
# 启动服务 systemctl start nat # 停止服务 systemctl stop nat # 重启服务 systemctl restart nat # 查看服务状态 systemctl status nat # 开机自启 systemctl enable nat # 取消开机自启 systemctl disable nat修改配置
修改配置文件后,程序会在 60 秒内自动应用新配置 ,无需手动重启服务。
# TOML 版本 vim /etc/nat.toml # 传统版本 vim /etc/nat.conf查看日志
# 实时查看日志 journalctl -fu nat # 查看详细日志 journalctl -exfu nat # 查看最近 100 行日志 journalctl -u nat -n 100查看 nftables 规则
# 查看所有规则 nft list ruleset # 仅查看 NAT 表 nft list table ip self-nat nft list table ip6 self-nat6🔧 高级配置
自定义源 IP (多网卡场景)
默认使用 masquerade 自动处理 SNAT 。如需指定源 IP:
# 设置自定义源 IP echo "nat_local_ip=10.10.10.10" > /opt/nat/env # 重启服务 systemctl restart nat🐋 Docker 兼容性
本工具已与 Docker 完全兼容。程序会自动调整 nftables 规则以适配 Docker 网络。
说明 :Docker v28 将 filter 表 forward 链默认策略改为 DROP ,本工具会自动将其重置为 ACCEPT 以确保 NAT 规则正常工作。
📌 注意事项
REDIRECT 类型限制
REDIRECT类型工作在 PREROUTING 链,仅对外部流量有效:
- ✅ 有效 :外部机器访问重定向端口 → 成功重定向
- ❌ 无效 :本机进程访问重定向端口 → 不会重定向
原因 :本机流量直接进入 OUTPUT 链,不经过 PREROUTING 链。
示例 :
# 配置:REDIRECT,8000,3128 curl http://remote-server:8000 # ✅ 成功重定向到 3128 curl http://localhost:8000 # ❌ 不会重定向,直接访问 8000TLS/Trojan 转发
转发 TLS/Trojan 等加密协议时,常见问题是证书配置错误。
解决方案 :
- 简单 :客户端禁用证书验证
- 推荐 :正确配置证书和域名,确保证书域名与中转机匹配
📄 许可证
本项目采用 MIT License 开源协议。
🔗 相关链接
- 项目地址 : https://github.com/arloor/nftables-nat-rust
- 问题反馈 : https://github.com/arloor/nftables-nat-rust/issues
- 前代项目 :arloor/iptablesUtils(不兼容)
注意 :与旧版 iptablesUtils 不兼容,切换时请先卸载旧版或重装系统。
作者: arloor | 发布时间: 2026-01-10 12:46
30. OpenCode 如何
号称开源的 Claude Code 一直滑到,有没有实际使用的分享下体验
作者: iwdmb | 发布时间: 2026-01-10 06:41
31. 腾讯云有没办法更换商务经理?
想通过一个腾讯云的服务商优惠价购买服务器,来回折腾才知道一个腾讯云账户只能绑定一个商务,而我的账户的商务角色绑定了腾讯云自家的一个商务经理。这个商务经理平时也没啥搭理我,只是在双 11 这类人人都知道的促销活动时给我发活动通知(有一次问我促销活动来了有没有购买需求,我报了需求,结果给我发了官网首页直接可以找到的活动链接告诉我直接下单,汗…)
于是找腾讯云售后问如何更换商务经理,得到的答复是不能换。请问是真的不能换吗?有没有 v 友有成功更换商务经理的经历可以分享下方法?
我知道可以通过新开腾讯云账户来绑服务商,但我是想让计划新购的服务器与老服务器内网互通,为了这个优惠把老服务器业务搬到新账户下又觉得折腾成本太高……
作者: Chaopi | 发布时间: 2026-01-09 14:12
32. [想赚点铜币] 教程之如何在 wsl 等远程不方便代理的环境,不使用 Antigravity 但是使用其中模型的额度
- Step 0. 前置准备
- 账号问题
- 网络问题
- Step 1. 准备 google pro 账号
- [选择 1:学生会员(成本低)](#选择 1 学生会员成本低)
- [选择 2:拼车](#选择 2 拼车)
- 准备一些小号
- Step 2. 配置 CLIProxyAPI
- Step 3. 配置 Claude Code 或其他 API 客户端
由于部分内容和本帖相关性不那么高,我就不多赘述了,请善用搜索引擎或者直接 Ask LLMs
Step 0. 前置准备
账号问题
注意检查以下几个问题:
- 账号的地区是否在美国或其他支持国家(比如新加坡)
- 账号的 Play 付款方式是否删除了香港或者大陆
- 是否过了 google 年龄验证(不是你资料里填了生日就行)
- 是否在 google cloud console 创建了项目并开启了 Gemini 相关的 API
网络问题
- 在 github 上找 google/gemini 相关规则(比如 ACL4SSR )
- 编辑 clash 配置文件,路由这部分流量到目标区域
- 最好使用 select 类型的配置组,不要使用 load-balance 让你的节点在美国各地乱跳
- 不要使用诸如 1 元机场这种万人骑的公交车
Step 1. 准备 google pro 账号
选择 1:学生会员(成本低)
Google 为学生会员准备了一年的免费 pro 权益
- 注意你即使真是学生,也不要用自己的学校邮箱注册,因为中国学校并不在支持范围内。
- 可以使用类似 1key.me 的注册机过验证,注意 IP
- 最麻烦的一步是绑定银行卡,可能会扣 1 刀乐验证,这部分建议咸鱼解决,同样不建议绑真卡
选择 2:拼车
鼠鼠没钱,所以这部分跳过
准备一些小号
- 由于 google ai pro 允许共享给家庭组(最多 5 人)使用,且不同人的额度独立
- 所以你可以准备一些小号,然后喜获额度 * 小号数量
- 注意小号也要满足前置条件,自己接码可能会被立即风控,建议找专业号贩子
Step 2. 配置 CLIProxyAPI
在一个多月前 Antigravity 发布的时候,我在这篇帖子里提到了代理问题很麻烦,即 code server 走 server 的网络而不是本地网络,并且 Antigravity 还不能配置代理:
后来的一个月时间网上出了各种教程贴,诸如 proxifier 等,但是都没有解决需要在 WSL 等远程环境下使用代理的问题。 有方法是替换服务器上的程序文件并附加代理,这显然不够优雅,而且每次 IDE 更新都要重新操作一遍。
好在有各位大佬开发了 CLIProxyAPI 这个工具,将 Antigravity 的认证和请求功能剥离出来。 有了它,你不需要安装 Antigravity 的 GUI 客户端 ,就能在任何环境下使用 Antigravity 模型。
安装
参考 https://help.router-for.me/introduction/quick-start.html
这里以 linux 为例: 默认会安装在
~/cliproxyapi/目录下,并且会创建一个 systemd 服务在~/.config/systemd/user/cliproxyapi.service配置 config.yaml
运行前需要先编辑
~/cliproxyapi/config.yaml文件,必须的配置项如下:# 使用这里的 key 再次分发(即配置到你需要使用 API 的地方) # 不是 Antigravity/Gemini 等官方服务的 key ,你自己生成 api-keys: - "your-proxyapi-key-here" # 代理地址,可以不在本地 proxy-url: "http://127.0.0.1:7890" # WebUI 控制台的登录密码 # 第一次运行后会被删除,请妥善保管 remote-management: secret-key: "your-management-keyword-here"其余的配置项可以看文档或者直接在 WebUI 里配置。
认证
./cli-proxy-api --antigravity-login --no-browser
- 运行后会打印一个 OAuth 链接,复制到浏览器打开并登录 Google 账号授权。
- 授权成功后不要按照提示关闭浏览器窗口,复制地址栏的完整回调 URL 回到终端粘贴回车。
- 如果有多个账号,可以重复运行上面的命令添加更多账号。
运行
systemctl --user start cliproxyapi
浏览器打开
http://localhost:8317访问 WebUI 控制台
- IP 是你运行 CLIProxyAPI 机器的地址
- 端口号默认是 8317 ,可以在 config.yaml 里修改
在
认证文件管理页面可以看到已经添加的账号
- 点击
模型管理可以看到已经解锁的 Gemini 模型
- 如果没有看到模型,说明账号不符合条件,请看前置准备之账号问题
- 配置模型名称映射(可选)
- Antigravity 不同模型间的配额独立,可以根据需要给常用模型起个别名,然后均衡使用不同模型
- 以下是我自己的映射配置示例:
oauth-model-mappings:
antigravity:
- name: gemini-3-pro-preview
alias: sonnet
fork: true
- name: gemini-3-flash-preview
alias: haiku
fork: true
- name: gemini-claude-sonnet-4-5-thinking
alias: opus
fork: true
基础设置
路由策略选择round_robin自动轮询多个账号,防止单个账号额度耗尽或者爆并发- 可以启用使用统计,从而查看你消耗的 token 数量,如果日后需要购买中转服务可以有个预估
Step 3. 配置 Claude Code 或其他 API 客户端
为什么我倾向于 Claude Code? 可能是因为 CC 生态更完善一些,而且我个人用久了很多东西配好了不想换。
懒人配置
比如我,直接打开
~/.claude/settings.json"env": { "ANTHROPIC_AUTH_TOKEN": "your-proxyapi-key-here", // 注意修改这个地址,是你运行 cliproxyapi 机器的地址 "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "haiku", "ANTHROPIC_DEFAULT_SONNET_MODEL": "sonnet", "ANTHROPIC_DEFAULT_OPUS_MODEL": "opus" }高阶配置
如果你的 provider 比较多,除了今天的 cliproxyapi 之外还有其他的 API 提供商,比如 GLM 或者其它各种中转站,可以使用下面这个项目:
https://github.com/SaladDay/cc-switch-cli/blob/main/README_ZH.md
Hello World
运行 claude ,试着打个招呼看看配置是否成功~
作者: QS0x01 | 发布时间: 2026-01-10 05:07
33. google ai pro 99.99 美刀一年值得购买吗
不想折腾学生白嫖的原因:
1. 懒得搞各种学生的虚假认证,想想就头大
2. 比较爱惜自己手上的 google 账号,担心搞了学生后指不定哪天被风控了,限制一些功能。
作者: YanSeven | 发布时间: 2026-01-06 06:55
34. 小米 13 手贱升了 hyperos3,崩了
不是米粉也不是米黑。
一个月前小米 13 手贱升了 hyperos3,电池续航从一天降到 0.3 天,快充也没了,和家人打打视频电话( volte/nr )动不动就卡画面,只能换微信打,各种小 bug,又降不回去,也没有 root 。
失望,准备 ld 淘汰的 iphone13 当主力机了。
作者: ipfox | 发布时间: 2026-01-08 23:55
35. 2025 年代码统计报告,程序员同行们,也来晒晒你们的报告吧
程序员同行们,晒晒你们的 2025 年代码统计,我使用的统计工具是 annualstats
![]()
作者: me876 | 发布时间: 2026-01-09 04:03
36. Claude Code Skill + K8S 安全审计有没有搞头?
Claude Code Skill 话题这么火,我也来凑个热度 🚀
开发了一个 kube-audit-kit.
🛡️ 它能做什么:
- 一键扫描 K8s 集群安全隐患
- 基于 PSS/NSA/CIS 权威安全标准
- 智能分组应用,静态扫描 + AI 深度分析
- 覆盖 Pod 安全、RBAC 、网络策略、敏感数据等
🎯 解决什么痛点:
- ❌ 手动翻 YAML 检查安全配置太耗时
- ❌ 不清楚集群里有哪些违规配置
- ❌ 想审计但怕影响生产环境
- ✅ 纯只读操作,零侵入,3 分钟出报告
欢迎试用,Star 支持 ⭐️
作者: lianglin999 | 发布时间: 2026-01-10 01:36
37. 搞了一台日版 Docomo 合约机 sony,有办法升级 Android 吗?
搞了一台日版 Docomo 合约机 sony Xperia 1 v ,有办法升级 Android 吗?
目前在手机更新,提示最新的 Android14 ,但据说这个已经 Android15 了?
作者: kobeguang | 发布时间: 2026-01-09 04:03
38. 求大佬们分享下跨境组网方案
人在大马,需要经常访问国内家里的服务器(无公网 ip )
手上还有一台香港 CN2 机器
预期是手上的各种移动设备都能无缝访问内网,所以用上了 tailscale
目前尝试的方案:
大马——tailscale p2p——内网:可以打洞成功,但晚高峰几乎不可用
大马——tailscale——香港,香港 frps——hy2——内网 frpc:小火箭直接连 hy2 节点是正常的,但加入 tailscale 后就不行了,涉及到透明代理,折腾一下午没搞定,放弃
大马——tailscale——香港自建 derp+peer relay ——内网:可用,但不稳定,经常存在过一段时间不可用的情况大马——tailscale——香港,香港 frpc——openvpn——内网 frps:这是多天尝试下来最好用的方案,但 openvpn 容易被识别,担心用两天就被封了
问 ai 也没啥好方案,求大佬们支招
作者: 0xfb709394 | 发布时间: 2026-01-08 09:25
39. 时间充裕,有什么软件暴力猜 xlsx 文件密码?
求推荐
作者: gtese | 发布时间: 2026-01-09 05:48
40. AI 编程工具现在需要这样造势了么?
题目是《淘宝内核组 001 号员工,20 年经验“小菜鸟”:我用 AI 写代码,但不担心“手艺”退化》,提到 AI 使用是最后一个提问:
CSDN:您个人在日常开发中,会使用类似 Cursor 大模型编程工具吗?您如何看待它对开发者“手艺”的影响?是颠覆性的助手,还是可能导致开发者基础能力退化的“慢性毒药”?
李勇:我是会使用到 Cursor ,还有字节的 Trae ,主要是在阅读代码时帮我解释一下某些简单函数的运行逻辑。实际工作中,这大大提高了我在学习一个全新内核子系统过程中的效率,确实很有用。 但是也要警惕这些工具出现幻觉,所以还需要具备一定的逻辑判断能力,不能完全相信这些编程工具的输出内容。 我本人不在意“手艺”退化的影响,毕竟大学毕业后我就已经不具备纸上写代码的能力了。 系统软件开发者,和普通的程序语言开发程序员的差别之一,是需要对整个系统有深刻的理解,然后除了实现功能外,还要兼顾考虑到硬件体系结构和软件架构相关的很多隐含的背景知识。可靠的辅助编程工具,可以将开发者从具体代码开发的繁琐细节中解放出来,更多的精力可以集中在代码思路、效率和更好的思路上,对 Linux 内核开发应该是会有积极的促进作用。但开发者个人要对最终的代码负责,要确保代码的品质,避免对 AI 工具的滥用。
?哪里有“我用 AI 写代码”了?还是说这时候把“读代码”归到“写代码”活动中了?
作者: xuanwu | 发布时间: 2026-01-09 23:47



