跳到主要内容

224 篇博文 含有标签「iCoding」

个人简介

查看所有标签

i18n 多语言接入使用文档

· 阅读需 17 分钟
Quany
软件工程师

1. 概述

涵盖场景:前端固定标签、后端消息、字典数据、业务数据、邮件模板。统一存库system_i18n_translation),前端通过接口按需加载;业务 key 以数据库为准,common/ui 等由框架或本地 base 提供。

自动化脚本:前端固定标签(extract:i18n)、后端消息(sync:i18n-message)、字典数据(sync:i18n-dict)均支持脚本自动化,免 token、免人工。


2. 前端固定标签翻译

代码中的固定文案(按钮、表单标签、表格列头、提示等)通过提取脚本写入数据库(scene_type=1),前端经 GET /system/i18n-pack/load?locale=xx 加载并与本地 base 合并。

需要对哪些使用 $t()

凡是在界面上直接展示给用户的中文(或需多语的)固定文案,都应使用 $t('key', { defaultValue: '...' }) 包裹,以便被提取并参与多语。具体包括:

场景说明示例位置 / 写法
页面/弹窗/区块标题列表页标题、详情页标题、抽屉/弹窗标题、路由 meta.title、Tab 标签、步骤条标题、卡片/区块标题Pagetitlemeta.title、弹窗 titletabs[].labelsteps[].title
表格列头表格列的 labelcolumns[].label、schema 中列定义 label
表单标签表单项前的文字、详情页字段名schema[].labelformOptionsfield.label、详情 description 的 label
占位符输入框、选择框的 placeholderplaceholdercomponentProps.placeholder
校验提示表单校验失败时的提示、zod 等校验 messagerulesz.string().min(1, $t(...)){ message: $t(...) }
按钮文字主按钮、行内操作、工具栏按钮(新增/导出/导入等)按钮文本、#extra / #actionstoolbarConfig 中按钮文案
操作/链接文案复制、编辑、删除、查看等操作名操作列、下拉菜单项、链接文本
选项标签非字典的固定选项(如业务线、业态等静态选项)options[].label{ value, label: $t(...) }
确认对话框确定/取消按钮、对话框标题与正文(如「确定删除选中的 x 条吗?」)confirm($t(...))Modal.confirm 的 title/content/okText/cancelText
提示/反馈成功、失败等 toast/message 文案message.success($t(...))message.error($t(...))
空状态文案表格/列表无数据、下拉无选项时的提示emptyText、空状态组件文案、「暂无数据」等
帮助与说明表单项下方的 helpText、图片等组件的 mask/tooltip、表格列 helphelpTextcomponentProps.mask、列定义 help

不必用 $t():字典项用 useDictOptions/dict-tag(走 dict_{dictType}_{value});后端返回的 msg 由后端翻译;纯数字/ID/占位符变量不参与多语。

格式:提取脚本只识别 $t('key', { defaultValue: '...' })(key 与 defaultValue 必填)。带插值用命名参数,如 $t('key', { defaultValue: '共 {count} 条', count: 变量 }),占位符名与参数键一致。

使用方式

  • 需被提取$t('key', { defaultValue: '...' })(提取脚本只识别该形式)。
  • key 已存在(如 ui.actionTitle.edit、路由 meta.title):可写 $t(key),不传第二参数。
  • 带替换:命名参数 $t(key, { defaultValue: '…', count: a }) 可提取;数组 $t(key, [a,b]) 对应 {0}{1},key 须已在语言包。模板字符串 `共 ${total} 条` 提取后变为 {0},调用时传 0: total
<h1>{{ $t('web.crmLead.assignRuleForm.title.edit', { defaultValue: '编辑自动分配规则' }) }}</h1>
$t('ui.actionTitle.edit', ['员工'])
$t('web.crmLead.poolImport.message.importSuccess', {
defaultValue: '导入成功:共处理 {count} 个文件,导入 {success} 条线索',
count: files.length,
success: result.successCount || 0,
})

翻译键命名

建议:模块.功能.组件.属性,如 web.crmLead.assignRuleForm.title.editcommon.button.save。defaultValue 在 key 不存在时作为展示文案,存在时用语言包译文。

提取与同步

执行方式:在项目根目录下执行:

cd yunfun-ui-admin-vben
pnpm run extract:i18n

脚本 scripts/extract-i18n-keys.mjs:扫描 apps/web-antd/srcpackages/**/src 下 .vue/.tsx/.jsx/.ts/.js,识别 $t('key', { defaultValue: '...' })(支持单/双引号、模板字符串;模板中 ${expr}{0}{1});将 zh-CN 上传至 {API_BASE_URL}/admin-api/system/i18n-pack/upload,写入 scene_type=1,并调自动翻译为 i18n.languages 中其他语言。默认不写本地文件(WRITE_LOCAL_FILES=true 时写 zh-CN 的 JSON)。

覆盖策略:脚本采用合并模式overwrite=false):数据库中已存在的 key 会被跳过、不覆盖;只有本次扫描到且库中不存在的 key 才会新增入库并触发翻译。适合多人协同开发,可放心多次执行,不会破坏已有译文。

新增键流程:代码中写 $t('key', { defaultValue: '...' }) → 执行 pnpm run extract:i18n → 新键入库,前端 load 时生效;可在 系统管理 → 国际化翻译 → 开发配置 → 语言包管理 查看。

环境变量(脚本请求带 admin-api 前缀):

变量名说明默认值
API_BASE_URL后端根地址.env.developmentVITE_BASE_URLhttp://localhost:48080
API_TOKEN登录态 Token
API_TENANT_ID租户 ID1
AUTO_TRANSLATE是否自动翻译true
WRITE_LOCAL_FILES是否写本地 zh-CN JSONfalse

模块识别(用于上传分组/本地文件名):web.crmLead.*→crm,web.systemUser.*→system 等;key 含点取第一段,否则 common。库内为扁平 key-value,load 接口按 locale 返回。


3. 后端消息翻译

异常由 GlobalExceptionHandler 拦截,用 translationKey = "ERROR_" + ex.getCode()I18nMessageUtil.getMessage,返回译文或原文。前端直接展示接口返回的 msg 即可。

管理系统管理国际化翻译开发配置翻译记录,筛选「后端消息」或前缀 ERROR_,可批量翻译或单条编辑。

错误码与消息翻译规范

  • 翻译键ERROR_ + 错误码数字(如 1003001000ERROR_1003001000),与 GlobalExceptionHandler 约定一致;错误码段遵循 ServiceErrorCodeRange
  • 存储system_i18n_translationscene_type=2。每条错误码至少一条 zh-CN 源语言(source_text/translated_textErrorCode.getMsg() 一致),唯一约束 (translation_key, target_lang, tenant_id, deleted)
  • category:1 异常 / 2 验证 / 3 业务 / 4 系统;业务异常建议 1。
  • 接入:业务中 throw exception(ErrorCodeConstants.XXX);脚本自动提取并同步,或在翻译记录页手动新增/修复翻译。

后端消息自动化流程(推荐):

  • 脚本自动化:执行 pnpm run sync:i18n-message,脚本会:1)从 Java 源码提取错误码;2)调用 sync-error-codes-to-i18n 写入翻译表;3)调用 auto-translate 自动翻译。免 token、免人工。
  • 页面手动修复翻译系统管理国际化翻译开发配置翻译记录,筛选「后端消息」或前缀 ERROR_,勾选后 批量翻译 或单条编辑。

4. 字典数据翻译

翻译键:dict_{dictType}_{value}。接口:GET /system/i18n-translation/get-text?translationKey=...&targetLang=...,返回译文字符串。

使用useDictOptions(dictType, valueType) 取选项列表(自动翻译+缓存);dict-tag 展示标签。手动调用:getTranslationText(translationKey, currentLang),返回字符串,失败用 originalLabel 兜底。

存储system_i18n_translationscene_type=3

字典自动化流程(推荐):

  • 前置字典管理 中已存在相关字典类型及数据。
  • 脚本自动化:执行 pnpm run sync:i18n-dict,脚本会:1)调用 sync-dict-to-i18nsystem_dict_data 同步到翻译表;2)调用 auto-translate 自动翻译。免 token、免人工。
  • 页面手动修复翻译系统管理国际化翻译开发配置翻译记录,筛选「字典数据」,勾选后 批量翻译 或单条编辑。

零散补充字典管理 中进入某条字典编辑,表单底部点击 翻译,可写入 zh-CN 并触发翻译。


5. 业务数据翻译

后端 TranslationAspect + TranslationService.handleQuery 按当前用户语言将业务字段翻译后返回,前端正常调业务 API 即可。翻译键:{client}.{module}.{domain}.{name}(如 web.crm-lead.leadTodo.todoContent)。

后端接入方式(按当前实现)

  • 对象上加领域注解:在返回给前端的 VO / CMD / CO 类上标 @TranslateDomain(module = "crm-lead")@TranslateDomain(module = "system") 等,用于声明所属模块。
  • 字段上加业务数据注解:在需要做多语的字段上标 @Translate(domain = "leadTodo", name = "todoContent", id = "id") 这类注解,domain 对应业务域,name 为字段名,id 为主键字段名(默认 id)。当前项目只使用字段级 @Translate,方法级 @Translate 作为开关能力暂未使用。
  • 业务数据多语:仅依赖 VO 上的 @TranslateDomain + @Translate 注解,无需额外配置。TranslationAspect 在 Controller 方法返回后扫描注解并回填翻译。
  • 查询时自动替换:用户语言为非 zh-CN 时,切面(TranslationAspect)按 fieldKey + dataIdsystem_i18n_business_data / system_i18n_translation 批量取译文,仅对存在翻译记录的字段做替换,未配置或无译文的字段保持原值。

前端行为与管理

  • 表单录入组件(I18nInput):在需要录入可多语的业务字段时,使用 @vben/common-ui 提供的 I18nInput(例如在线索池 / 线索个人 / 线索待办等编辑页中),通过 module / domain / 字段名等配置,与后端 @TranslateDomain / @Translate 一一对应,统一录入「原文 + 各目标语言」。
  • 业务接口调用:普通业务页面(如线索列表、来源管理)只需调用原有业务 API,即可拿到已按用户语言翻译后的字段,无需在前端额外 $t() 或手动调用翻译接口。
  • 业务数据翻译管理页:在 系统管理 → 国际化翻译 → 翻译工作台 → 业务数据翻译 中,可按模块/域/字段、fieldKeydataId 查询、编辑和批量维护业务数据翻译。
  • 前端手动调用(可选):如需在自定义页面中手工维护某条业务数据的翻译,可使用 #/api/system/i18n/business-data 中的接口,例如:getBusinessDataList(fieldKey, dataId) 查询现有翻译,saveBusinessDataTranslation({ fieldKey, dataId, sourceText, sourceLang, targetLangTranslations }) 保存修改。

列表页多语搜索(I18nSearchHelper)

列表页按多语字段搜索时,需同时匹配主表字段与 i18n 翻译表(source_texttranslated_text)。I18nSearchHelper 提供统一的多语搜索能力。

接入方式

  • PageReqVO 实现接口:分页请求 VO 实现 I18nSearchable,Service 注入 I18nBusinessDataService
  • Service 层:调用 enrichWithFields(全字段模糊)或 enrich(支持 per-field fuzzy),从 i18n 表反查 data_id 填充到 VO:
// 全字段模糊匹配(从 voClass 解析 domainPrefix)
I18nSearchHelper.enrichWithFields(pageReqVO, LeadPoolRespVO.class,
List.of("brandName", "contactName", "mobile"),
fieldName -> switch (fieldName) {
case "brandName" -> pageReqVO.getBrandName();
case "contactName" -> pageReqVO.getContactName();
case "mobile" -> pageReqVO.getMobile();
default -> null;
},
i18nBusinessDataService);

// 部分字段精确匹配(从 voClass 解析 fieldKey)
I18nSearchHelper.enrich(pageReqVO, LeadTodoRespVO.class,
List.of(
I18nSearchFieldConfig.Spec.field("todoContent"), // 模糊
I18nSearchFieldConfig.Spec.field("brandName", false) // 精确
),
fieldName -> switch (fieldName) {
case "todoContent" -> pageReqVO.getTodoContent();
case "brandName" -> pageReqVO.getBrandName();
default -> null;
},
i18nBusinessDataService);
  • Mapper 层:使用 getI18nIds 获取 data_idapplyI18nLikeCondition / applyI18nEqCondition 构建条件 (主表字段 LIKE/EQ 搜索词) OR (id IN i18nIds)
I18nSearchHelper.applyI18nLikeCondition(wrapper, reqVO.getBrandName(),
I18nSearchHelper.getI18nIds(reqVO, "brandName"),
LeadMainDO::getBrandName, LeadMainDO::getId);

field_key 约定{client}.{module}.{domain}.{name}(如 web.crm-lead.leadPool.brandName)。可使用 TranslationAnnotationUtils.getFieldKeyFromAnnotation(voClass, fieldName, client)@Translate 注解解析。

业务数据删除时自动清理多语

在 Service 层的删除方法上添加 @CleanupI18nOnDelete 注解,业务数据删除成功后会自动清理对应的多语数据。

注解参数

参数必填说明
module模块编码(如 "crm-lead"),对应 @TranslateDomainmodule
domain业务域(如 "leadTodo"),对应 @Translatedomain
doClass业务 DO 类,用于获取表名,清理时按 business_table_name 过滤,兼容历史数据
dataIdParam数据ID参数名,默认 "id",支持 SpEL(如 "#reqVO.id"
batch是否批量删除,默认 false

示例

// 单个删除(指定 doClass 便于按表名过滤)
@Override
@CleanupI18nOnDelete(module = "crm-lead", domain = "leadTodo", doClass = LeadTodoDO.class)
@Transactional(rollbackFor = Exception.class)
public void deleteLeadTodo(Long id) {
leadTodoMapper.deleteById(id);
}

// 批量删除
@Override
@CleanupI18nOnDelete(module = "crm-lead", domain = "leadTodo", batch = true, dataIdParam = "ids")
@Transactional(rollbackFor = Exception.class)
public void deleteLeadTodos(List<Long> ids) {
leadTodoMapper.deleteBatchIds(ids);
}

业务数据多语快照(审批场景)

适用场景:审批流程中需要暂存变更的多语数据,待审批通过后再正式生效。例如客户变更审批、品牌变更审批等场景。

@Translate 的区别

特性@Translate@TranslateSnapshot
存储位置system_i18n_business_data(业务多语表)system_i18n_business_data_snapshot(快照表)
生效时机立即生效审批通过后同步生效
适用场景普通业务数据保存/查询审批流程中的暂存数据
同步方式自动保存/自动查询回填手动调用同步接口

后端接入方式

  1. VO/CMD/CO 类上加领域注解:与 @Translate 相同,使用 @TranslateDomain(module = "crm-customer") 声明所属模块。

  2. 字段上加快照注解:在需要做多语的字段上标 @TranslateSnapshot,配置与 @Translate 基本一致,额外支持 doClass 指定 DO 类用于自动获取表名:

@TranslateDomain(module = "crm-customer")
public class CustomerSaveReqVO {

@TranslateSnapshot(domain = "customer", name = "customerName", id = "id", doClass = CustomerDO.class)
private String customerName;

@TranslateSnapshot(domain = "customer", name = "customerDesc", id = "id", doClass = CustomerDO.class)
private String customerDesc;

// 多语 Map 字段,后缀默认 ML
private Map<String, String> customerNameML;
private Map<String, String> customerDescML;
}
  1. Controller 方法上加注解:在需要启用快照处理的 Controller 方法上加 @TranslateSnapshot 作为开关(方法级注解本身不需要配置 domain/name,只作为开关使用):
@RestController
@RequestMapping("/crm/customer")
public class CustomerController {

@PostMapping("/save")
@TranslateSnapshot // 启用快照处理
public Result<Long> saveCustomer(@RequestBody @Valid CustomerSaveReqVO reqVO) {
return success(customerService.saveCustomer(reqVO));
}
}
  1. 同步机制:审批通过后,调用 I18nBusinessDataSnapshotService.syncSnapshotToBusinessData 将快照数据同步到业务多语表:
@Service
@RequiredArgsConstructor
public class CustomerApprovalService {

private final I18nBusinessDataSnapshotService snapshotService;

/**
* 审批通过回调
*/
public void onApprovalPassed(Long customerId) {
// 将快照表数据同步到业务多语表,正式生效
snapshotService.syncSnapshotToBusinessData("crm_customer", customerId.toString());
}
}

关键说明

  • 保存时:切面自动识别 @TranslateSnapshot 标记的字段,将多语数据写入快照表(以 business_table_name + data_id 为维度)。
  • 查询时:切面自动从快照表读取当前用户语言翻译并回填字段,同时填充 xxxML Map 供前端编辑回显。
  • dataId 获取:优先从 idMethod 指定的方法获取,其次从 id 指定的字段获取,新增数据时若 ID 为空则跳过保存。
  • 表名获取:优先从 doClass@TableName 注解读取,其次尝试从 VO 类名推断对应 DO 类。

📝 示例

表单/按钮/表格列:$t('web.crmLead.xxx.field.name', { defaultValue: '规则名称' })$t('common.button.save', { defaultValue: '保存' })、列 label: $t('web.crmLead.table.column.name', { defaultValue: '名称' })

业务数据多语表单(I18nInput + 注解对应关系,示例摘自线索公海):

<I18nInput
:model-value="field.value"
@update:modelValue="(val: string) => field.onChange?.(val)"
v-model:modelValueMl="companyNameML"
module-code="crm-lead"
domain="leadPool"
name="companyName"
:data-id="formData?.id ? Number(formData.id) : 0"
@translate="(p) => handleI18nTranslate('companyName', p)"
:placeholder="$t('web.crmLead.poolForm.placeholder.companyName', {
defaultValue: '请输入公司名称',
})"
/>

对应后端 VO 上的配置大致为:

@TranslateDomain(module = "crm-lead")
public class LeadPoolRespVO {

@Translate(domain = "leadPool", name = "companyName", id = "id")
private String companyName;
}

6. 邮件模板翻译

发送时按收件用户语言(用户表 locale → 请求上下文 → 默认 zh-CN)取译文,键:mail.template.{模板code}.titlemail.template.{模板code}.content。存 system_i18n_translation,scene_type=5,category=4。

自动流程:保存/更新邮件模板时自动写 zh-CN 源语言、各目标语言占位,并触发按 mail.template.{code}. 前缀的批量翻译。业务无需单独调翻译接口。

管理系统管理国际化翻译开发配置翻译记录,筛选「邮件模板」或前缀 mail.template.,可批量翻译或单条编辑。占位符(如 {name})翻译时保留。


❓ 常见问题

问题答案
如何查看语言包?系统管理 → 国际化翻译 → 开发配置 → 语言包管理 / 翻译记录(按 scene_type 筛选);前端切换语言时自动 load。
extract:i18n 报错?脚本依赖后端,需启动服务、API_BASE_URL 正确,需登录时设 API_TOKEN;仅上传不翻译可设 AUTO_TRANSLATE=false
只翻译部分语言?平台配置 i18n.languages 只保留需要的语言即可。
是否保留 locales/langs?先加载本地 base + 框架 common/ui,再合并 load 接口数据;业务 key 不要求维护本地 JSON,可选 WRITE_LOCAL_FILES=true 写 zh-CN 快照。
支持语言在哪配置?平台配置 i18n.languages(JSON 数组)。
web.crmLead.* 对应模块?脚本识别为 crm 模块(上传分组/本地 crm.json);库内为扁平 key。
load 返回结构?扁平 key-value,前端会转嵌套供 vue-i18n 使用。
固定标签存库吗?是。同表 system_i18n_translation,scene_type:1 前端UI / 2 后端消息 / 3 字典 / 4 业务数据 / 5 邮件模板。
sync:i18n-message / sync:i18n-dict 报错?脚本免 token、免 JWT,依赖 permit-all 配置;需重启后端使 sync-dict-to-i18nsync-error-codes-to-i18n 等接口的 permit-all 生效;确保后端已启动且 API_BASE_URL 正确。

附录:脚本说明

Node 脚本

yunfun-ui-admin-vben 目录下执行。

baseURL:默认 http://localhost:48080,可在 .envAPI_BASE_URL 环境变量中配置;接口路径固定为 {baseURL}/admin-api/system/i18n-pack/...

命令用途
pnpm run extract:i18n扫描前端 $t(),上传并自动翻译(scene_type=1)
pnpm run sync:i18n-message后端消息:从 Java 源码自动提取 ErrorCode 并同步到翻译表,再自动翻译(scene_type=2)
pnpm run sync:i18n-dict字典数据:先同步 system_dict_data→翻译表,再自动翻译(scene_type=3)
pnpm run sync:i18n-message-dict后端消息 + 字典一并执行

环境变量(免 token,依赖 permit-all 配置):

变量说明默认值
API_BASE_URL后端根地址默认 localhost:48080,可配 .env 或 env
API_TENANT_ID租户 ID1
SCENE_TYPES场景类型,逗号分隔2,3(2=后端消息,3=字典)

多租户时注意脚本内 tenant_id(默认 1),按实际租户修改或保持与登录租户一致。


7. 语言偏好与获取当前语言方式

前端语言标识

  • 所有请求统一通过 Accept-Language 头把当前语言传给后端:在请求拦截器中写入 config.headers['Accept-Language'] = preferences.app.locale,值与前端切换的语言代码一致(如 zh-CNen-US)。
  • 语言切换入口为右上角的 LanguageToggle 组件,使用 @vben/localesloadLocaleMessages 加载语言包,并更新 preferences.app.locale;同时将当前语言写入全局变量 window.__APP_LOCALE__,供 I18nInput 等通用多语组件识别主语言与译文顺序。

用户语言字段(system_users.language)

  • 用户表 system_users 新增 language 字段(如 zh-CNen-USja-JP),作为用户语言偏好存储。
  • 语言切换时,前端通过 updateUserProfile({ language: targetLocale }) 异步更新该字段;登录后会把 language 写入登录上下文,用于邮件模板等场景按用户偏好发送对应语言的内容。

后端获取当前语言的方式

  • 通用消息 / 错误提示:通过 I18nMessageUtil.getCurrentLanguage() 从当前请求的 Accept-Language 头解析出语言代码(优先级:Accept-Language > 默认 zh-CN),并在 GlobalExceptionHandler 中按此语言翻译错误码消息。
  • 字典多语DictDataController 的 simple-list 接口同样依赖 Accept-Language,对非 zh-CN 请求自动返回对应语言的字典标签,前端切换语言后通过刷新字典缓存重新拉取。
  • 业务数据多语TranslationAspect 内部按当前用户语言(优先:登录上下文中的 locale/language,否则回退到中文)决定是否替换业务字段的返回值;一般情况下,业务代码无需显式获取语言,只要正常走统一的切面与翻译服务即可。
  • 手动获取翻译文本:在需要后端手工调用翻译服务的场景,可使用 I18nMessageUtil.getMessage(key)(自动按当前请求语言返回译文)或 getMessage(key, lang)(显式指定语言),也可以调用 translationService.getTranslationText(translationKey, targetLang) 直接按 key + 语言取译文。

🍎 macOS Python 最佳安装与配置指南(2026版)

· 阅读需 5 分钟
Quany
软件工程师

1. AI 与人协同考核

这是没有现成开源方案的部分,需要你自己搭建。核心思路是:

考核维度 = AI 产出指标 + 人工协作指标

维度AI 侧指标人工侧指标数据来源
代码质量AI 生成代码的 Bug 率、安全漏洞数、测试覆盖率人工评审通过率、返工率Git 提交 + AI Review API
协作效率AI 响应时间、代码生成速度人工评审响应时间、迭代次数Webhook 事件日志
知识贡献AI 辅助的文档生成量人工代码注释质量、Wiki 更新仓库元数据
团队影响AI 建议采纳率跨团队协作指数、Bus FactorMR/PR 关联分析

开源组件可组合:

  • GitLabMetricsAnalyzer(.NET 9):实时计算开发者生产力指标,包括 Bus Factor、评审响应时间、跨团队协作指数等
  • gitlab-dora-metrics:计算 DORA 四大指标(部署频率、变更前置时间、变更失败率、恢复时间)
  • merge-mind:AI 代码审查机器人,带 Web 仪表盘和 Prometheus/Grafana 监控

2. Claude API 接入

这是最成熟的部分,已有多个开源实现:

项目说明特点
claude-code-gitlab官方文档级别的集成方案支持 @claude 触发审查、自动创建 MR、实现功能需求
RealMikeChong/claude-code-for-gitlabClaude Code 的 GitLab 适配版代码分析、PR 准备、代码审查
merge-mind自托管 AI 审查机器人支持 GPT-4,向量数据库存储,持续学习团队风格
GitButler Agents专为 AI 设计的 Git 客户端每个 AI 会话独立分支,自动提交,支持并行多 Agent

GitButler 是 GitHub 联合创始人 Scott Chacon 的新项目,2026 年刚完成 1700 万美元 A 轮融资,专为 AI Agent 协作重新设计版本控制——能追踪哪个 LLM 生成了哪段代码、用了什么 Prompt,这是传统 Git 无法做到的 。


3. 移动端适配的 Git 平台选择

这是你的核心痛点。GitLab 的移动端体验确实差,但替代方案也有明显短板

对比矩阵

平台开源移动端方案AI 改造友好度资源占用适合场景
GitLabOpen-core官方 App(功能有限)+ 第三方客户端如 GitAlchemy⭐⭐⭐ 生态最全,但笨重4GB+ RAM企业级,不差资源
Gitea✅ MIT无官方 App,依赖第三方如 GitTouch(2021 年后停更)⭐⭐⭐ 轻量,API 简单1GB RAM小而美,需自研移动端
Forgejo✅ 100% 自由软件同 Gitea(共享代码库)⭐⭐⭐ 社区驱动,兼容 GitLab CI1GB RAM重视开源伦理
GitButler✅ 免费开源桌面端为主(macOS/Linux),Windows 开发中⭐⭐⭐⭐⭐ 为 AI 原生设计轻量AI 深度改造首选

关键发现

没有"移动端体验好 + AI 原生 + 开源"三者兼备的平台。

  • Gitea/Forgejo:Web UI 响应快(0.4s 页面加载 vs GitLab 的 1.8s),但移动端完全依赖第三方 App,而第三方 App 要么停更(GitTouch 2021 年后无更新),要么功能简陋(Lab+ mostly view-only)
  • GitButler:唯一为 AI 时代重新设计的版本控制工具,但目前无移动端,只有桌面客户端和 CLI
  • GitHub:官方 App 体验最好,但不开源,且 Copilot/Spark 等 AI 功能闭源

推荐方案:分层架构

既然没有银弹,建议分层解耦

┌─────────────────────────────────────────────┐
│ 移动端层(需自研或选型) │
│ ┌─────────────┐ ┌───────────────────┐ │
│ │ PWA/Web App │或 │ 原生 App(Flutter)│ │
│ │ (响应式UI) │ │ 调用下层 API │ │
│ └─────────────┘ └───────────────────┘ │
│ ↓ 统一 REST/GraphQL API │
├─────────────────────────────────────────────┤
│ Git 平台层(可选) │
│ ┌─────────────┐ ┌───────────────────┐ │
│ │ Forgejo │或 │ GitLab CE │ │
│ │ (轻量快速) │ │ (功能最全) │ │
│ └─────────────┘ └───────────────────┘ │
│ ↓ Webhook + API │
├─────────────────────────────────────────────┤
│ AI 协同层 │
│ ┌─────────────┐ ┌───────────────────┐ │
│ │ Claude Code │ │ GitButler CLI │ │
│ │ (API调用) │ │ (Agent工作流) │ │
│ └─────────────┘ └───────────────────┘ │
│ ↓ │
├─────────────────────────────────────────────┤
│ 考核指标层(需自研) │
│ ┌─────────────────────────────────────┐ │
│ │ GitLabMetricsAnalyzer + 自定义指标 │ │
│ │ (DORA + AI/人工协同指标) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

我的建议

如果你坚持开源 + 移动端 + AI 深度改造:

  1. Git 平台选 Forgejo(比 Gitea 社区更活跃,支持 ForgeFed 联邦协议)
  2. 移动端必须自研——用 Flutter 或 PWA 封装 Forgejo API,这是最大投入点
  3. AI 层用 GitButler CLI 管理 Agent 工作流,配合 Claude API 做代码生成/审查
  4. 考核层用 GitLabMetricsAnalyzer 的指标模型,自己扩展 AI 协同维度

如果你愿意妥协移动端为 PWA/Web:

直接上 GitButler——它是目前唯一真正为"AI + 人"协同设计的版本控制工具,能追踪 AI 代码血缘、支持多 Agent 并行、自动整理提交 。但它目前只有桌面端。

如果你愿意妥协开源:

GitHub + Copilot Studio——官方 App 体验最好,AI 集成最深,可以训练自定义代码审查模型 ,但完全闭源。


一个关键问题

你的"办公考核"具体是什么场景?

  • 开发者绩效考核(代码产出、质量、协作效率)?
  • 还是AI Agent 的效能评估(AI 生成了多少有效代码、节省了多少人工)?
  • 还是混合团队管理(人类 + AI Agent 一起考核)?

这决定了指标层该怎么设计。如果是第三种(混合考核),GitButler 的"AI 代码血缘追踪"能力几乎是刚需 。

🍎 macOS Python 最佳安装与配置指南(2026版)

· 阅读需 6 分钟
Quany
软件工程师

基于当前最佳实践,推荐 pyenv + pipx + venv 组合方案,完美适配 Intel 和 Apple Silicon 芯片。

📋 方案对比

方案优点缺点推荐度
pyenv + pipx + venv版本隔离、工具隔离、项目隔离、最灵活配置稍复杂⭐⭐⭐⭐⭐
官网 .pkg 安装包简单直接无法多版本管理、可能污染系统⭐⭐
Homebrew 直接安装更新方便只能安装一个版本⭐⭐⭐
Conda科学计算友好体积大、生态相对封闭⭐⭐⭐

🚀 完整安装配置流程

第1步:安装 Homebrew(包管理器)

# 如果已安装可跳过
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 验证安装
brew --version

第2步:安装 pyenv(Python 版本管理)

# 安装 pyenv
brew update
brew install pyenv

# 配置 shell 环境(macOS Catalina 及以后默认使用 zsh)
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

# 使配置生效
source ~/.zshrc

# 验证安装
pyenv --version

第3步:安装编译依赖(防止编译失败)

# 安装 Python 编译所需依赖
brew install openssl readline sqlite3 xz zlib tcl-tk

# Apple Silicon 芯片额外配置(M1/M2/M3)
if [[ $(uname -m) == 'arm64' ]]; then
echo 'export LDFLAGS="-L/opt/homebrew/opt/zlib/lib -L/opt/homebrew/opt/openssl/lib"' >> ~/.zshrc
echo 'export CPPFLAGS="-I/opt/homebrew/opt/zlib/include -I/opt/homebrew/opt/openssl/include"' >> ~/.zshrc
source ~/.zshrc
fi

第4步:安装 Python 3(推荐最新稳定版)

# 查看可安装版本
pyenv install --list | grep "^\s*3\.[0-9]\+"

# 安装 Python 3.13.3(2026年4月最新稳定版)
pyenv install 3.13.3

# 设置为全局默认版本
pyenv global 3.13.3

# 验证安装
python --version # 应显示 Python 3.13.3
which python # 应显示 ~/.pyenv/shims/python

第5步:安装 pipx(全局命令行工具管理)

# 安装 pipx
brew install pipx
pipx ensurepath

# 或使用 pip 安装(如果 brew 不可用)
# python -m pip install --user pipx
# python -m pipx ensurepath

# 验证安装
pipx --version

第6步:安装常用开发工具

# 代码质量工具
pipx install black # 代码格式化
pipx install flake8 # 代码检查
pipx install mypy # 类型检查
pipx install isort # import 排序
pipx install ruff # 更快的代码检查(推荐)

# 项目工具
pipx install poetry # 现代依赖管理(推荐)
# 或 pipx install pdm # 另一种选择

# 实用工具
pipx install httpie # 现代 HTTP 客户端
pipx install cookiecutter # 项目模板生成器
pipx install jupyter # Jupyter Notebook

# 验证安装
black --version
poetry --version

🛠️ 项目级虚拟环境配置

使用 venv(Python 内置,推荐)

# 创建项目
mkdir myproject && cd myproject

# 创建虚拟环境
python -m venv .venv

# 激活虚拟环境
source .venv/bin/activate
# 激活后提示符前会显示 (.venv)

# 安装项目依赖
pip install requests flask pandas

# 退出虚拟环境
deactivate

使用 Poetry(现代项目管理)

# 创建新项目
poetry new myproject
cd myproject

# 添加依赖
poetry add requests
poetry add --dev pytest black

# 安装所有依赖
poetry install

# 进入虚拟环境
poetry shell

# 运行脚本
poetry run python main.py

📁 目录结构示例

~/
├── .pyenv/ # pyenv 安装的 Python 版本
│ ├── versions/
│ │ ├── 3.13.3/
│ │ └── 3.12.5/
│ └── shims/ # 命令重定向
├── .local/share/pipx/ # pipx 安装的工具
│ ├── venvs/
│ └── bin/
└── projects/
├── project1/
│ └── .venv/ # 项目虚拟环境
└── project2/
└── .venv/

🔄 多版本管理命令

pyenv 常用命令

# 查看所有可用版本
pyenv install --list

# 安装特定版本
pyenv install 3.12.5
pyenv install 3.11.9

# 查看已安装版本
pyenv versions
# 带 * 的是当前激活版本

# 切换版本
pyenv global 3.13.3 # 全局默认
pyenv local 3.12.5 # 当前目录生效
pyenv shell 3.11.9 # 当前终端会话生效

# 卸载版本
pyenv uninstall 3.10.0

# 刷新缓存
pyenv rehash

pipx 常用命令

# 安装工具
pipx install <package>

# 临时运行(不安装)
pipx run <package> [args]

# 列出已安装工具
pipx list

# 升级工具
pipx upgrade <package>
pipx upgrade-all

# 卸载工具
pipx uninstall <package>

# 查看工具环境
pipx environment

⚙️ 配置文件示例

~/.zshrc 配置

# Python 配置
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

# pipx 配置(如果使用 pip 安装)
export PATH="$HOME/.local/bin:$PATH"

# 虚拟环境提示符
export VIRTUAL_ENV_DISABLE_PROMPT=0

.gitignore 配置

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# 虚拟环境
.venv/
venv/
env/
ENV/
env.bak/
venv.bak/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

🎯 最佳实践建议

1. 版本选择策略

  • 生产环境:Python 3.12.x(LTS,长期支持)
  • 开发环境:Python 3.13.x(最新特性)
  • 兼容性要求:Python 3.8+(大多数库支持的最低版本)

2. 工具选择指南

场景推荐工具说明
代码格式化black + isort自动格式化,无需争论风格
代码检查ruff比 flake8 快 10-100 倍
类型检查mypy静态类型检查
依赖管理poetry现代、功能全面
虚拟环境venvPython 内置,简单可靠
CLI工具pipx全局工具隔离管理

3. 工作流示例

# 1. 创建新项目
mkdir awesome-project && cd awesome-project

# 2. 设置项目Python版本
pyenv local 3.13.3

# 3. 创建虚拟环境
python -m venv .venv
source .venv/bin/activate

# 4. 初始化项目(使用 poetry)
poetry init
poetry add fastapi uvicorn
poetry add --dev black ruff mypy pytest

# 5. 配置开发环境
echo ".venv/" >> .gitignore
echo "__pycache__/" >> .gitignore

# 6. 开始开发!

🔧 故障排除

常见问题解决

  1. pyenv 安装 Python 失败

    # 确保编译依赖已安装
    brew install openssl readline sqlite3 xz zlib tcl-tk

    # Apple Silicon 芯片设置
    export SDKROOT=$(xcrun --show-sdk-path)
  2. pipx 命令找不到

    # 重新配置 PATH
    pipx ensurepath
    source ~/.zshrc

    # 或手动添加
    echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
  3. 虚拟环境激活失败

    # 检查 Python 版本
    python --version

    # 重新创建虚拟环境
    deactivate 2>/dev/null
    rm -rf .venv
    python -m venv .venv
    source .venv/bin/activate

📊 环境验证检查表

# 运行以下命令验证环境
echo "=== Python 环境验证 ==="
python --version
which python
pyenv versions
pipx list
pip --version
echo "=== 完成 ==="

🎁 一键安装脚本

#!/bin/bash
# macOS Python 环境一键安装脚本

echo "安装 Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

echo "安装 pyenv..."
brew install pyenv

echo "配置 pyenv..."
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc

echo "安装编译依赖..."
brew install openssl readline sqlite3 xz zlib tcl-tk

echo "安装 Python 3.13.3..."
pyenv install 3.13.3
pyenv global 3.13.3

echo "安装 pipx..."
brew install pipx
pipx ensurepath

echo "安装开发工具..."
pipx install black ruff poetry httpie

echo "环境配置完成!"
python --version

📈 后续优化建议

  1. 配置 IDE:VS Code 安装 Python 扩展,配置使用 pyenv 环境
  2. 设置别名:在 ~/.zshrc 中添加常用命令别名
  3. 定期更新:每月运行 brew update && brew upgradepipx upgrade-all
  4. 备份配置:将 ~/.zshrc 和项目 requirements.txtpyproject.toml 加入版本控制

这套方案在 2026 年仍然是 macOS 上 Python 开发的最佳实践,兼顾了灵活性、隔离性和易用性。开始享受现代 Python 开发吧!🚀

🧪 软件测试分类详解

· 阅读需 8 分钟
Quany
软件工程师

📊 测试分类全景图

软件测试
├── 按测试方法分类
│ ├── 黑盒测试 (Black-box Testing)
│ ├── 白盒测试 (White-box Testing)
│ └── 灰盒测试 (Gray-box Testing) - 两者结合

├── 按测试阶段分类
│ ├── 单元测试 (Unit Testing)
│ ├── 集成测试 (Integration Testing)
│ ├── 系统测试 (System Testing)
│ └── 验收测试 (Acceptance Testing)

├── 按测试目的分类
│ ├── 功能测试 (Functional Testing)
│ ├── 性能测试 (Performance Testing)
│ ├── 安全测试 (Security Testing)
│ ├── 兼容性测试 (Compatibility Testing)
│ └── 可用性测试 (Usability Testing)

└── 按执行方式分类
├── 手动测试 (Manual Testing)
└── 自动化测试 (Automation Testing)

🔲 黑盒测试 vs 白盒测试

黑盒测试 (Black-box Testing)

核心思想:只关注输入输出,不关心内部实现

维度说明
测试视角用户视角,测试软件功能
关注点功能是否按需求工作
测试依据需求文档、规格说明书
测试人员测试工程师、用户
优点1. 从用户角度验证
2. 无需了解代码实现
3. 容易发现需求不一致问题
缺点1. 测试覆盖率难以量化
2. 无法测试内部逻辑
3. 可能遗漏代码路径
常用技术等价类划分、边界值分析、决策表、状态转换
适用阶段集成测试、系统测试、验收测试

典型场景

  • 测试登录功能:输入用户名密码,验证能否成功登录
  • 测试购物流程:添加商品、结算、支付,验证订单生成
  • 测试API接口:发送请求,验证响应数据和状态码

白盒测试 (White-box Testing)

核心思想:深入代码内部,测试逻辑结构和路径

维度说明
测试视角开发者视角,测试代码逻辑
关注点代码覆盖率、逻辑正确性
测试依据源代码、设计文档
测试人员开发工程师、测试开发工程师
优点1. 能发现深层次代码错误
2. 可量化测试覆盖率
3. 能测试所有逻辑路径
缺点1. 需要编程能力
2. 可能过度关注实现细节
3. 无法验证需求符合性
常用技术语句覆盖、分支覆盖、路径覆盖、条件覆盖
适用阶段单元测试、集成测试

典型场景

  • 测试函数的所有if-else分支
  • 验证循环边界条件
  • 检查异常处理逻辑
  • 确保所有代码行都被执行到

🔗 串测(集成测试/端到端测试)

串测通常指集成测试端到端测试,主要验证多个模块/系统串联工作的正确性。

集成测试 (Integration Testing)

目的:验证模块/组件之间的接口和交互

测试策略说明优点缺点
大爆炸式所有模块一次性集成后测试快速、简单错误定位困难
自顶向下从顶层模块开始,逐步向下集成早期验证主要功能需要大量桩模块
自底向上从底层模块开始,逐步向上集成早期验证基础功能需要大量驱动模块
三明治式结合自顶向下和自底向上平衡效率和质量实现较复杂
持续集成每次代码变更都自动集成测试快速发现问题需要完善的自动化

端到端测试 (End-to-End Testing)

目的:模拟真实用户场景,验证完整业务流程

典型串测场景

  1. 用户注册流程

    前端页面 → API网关 → 用户服务 → 数据库 → 邮件服务 → 短信服务
  2. 电商下单流程

    浏览商品 → 加入购物车 → 填写地址 → 选择支付 → 扣减库存 → 生成订单 → 物流通知
  3. 微服务架构串测

    订单服务 → 支付服务 → 库存服务 → 物流服务 → 通知服务

🎯 三种测试的对比与应用

对比表格

测试类型测试对象测试人员测试依据关注点典型工具
黑盒测试完整系统/功能测试工程师需求文档功能正确性Postman, Selenium, JMeter
白盒测试代码/模块开发工程师源代码代码质量JUnit, pytest, JaCoCo
串测模块间交互测试/开发工程师接口文档流程完整性Postman, Cypress, K6

实际应用策略

1. 测试金字塔模型(推荐)

        ╱╲        UI测试/端到端测试 (少量)
╱ ╲
╱ ╲ 集成测试/串测 (适量)
╱ ╲
╱________╲ 单元测试/白盒测试 (大量)
  • 底层(最多):单元测试(白盒)
  • 中层(适量):集成测试(串测)
  • 顶层(最少):UI测试(黑盒)

2. 测试选择指南

场景推荐测试类型理由
新功能开发白盒测试 → 黑盒测试 → 串测先保证代码质量,再验证功能,最后验证流程
API接口测试黑盒测试为主,白盒为辅关注输入输出,适当检查边界条件
业务流程测试串测为主,黑盒验证确保端到端流程通畅
性能测试黑盒测试从用户角度评估系统性能
安全测试黑盒+白盒结合外部攻击测试+代码安全审计

3. 测试用例设计示例

用户登录功能测试:

# 白盒测试用例(单元测试)
def test_login_logic():
# 测试各种分支
assert login("valid_user", "correct_pwd") == True
assert login("valid_user", "wrong_pwd") == False
assert login("", "any_pwd") == False # 空用户名
assert login("user", "") == False # 空密码
assert login(None, "pwd") == False # None处理
assert login("a"*101, "pwd") == False # 用户名超长

# 黑盒测试用例(功能测试)
"""
测试场景:
1. 正确用户名密码 → 登录成功,跳转首页
2. 错误密码 → 提示"密码错误"
3. 不存在的用户 → 提示"用户不存在"
4. 连续错误5次 → 账户锁定
5. 登录后刷新 → 保持登录状态
6. 不同浏览器登录 → 功能一致
"""

# 串测试用例(集成测试)
"""
测试流程:
1. 前端输入 → API调用 → 用户服务验证 → 数据库查询
2. 登录成功 → 生成token → 返回前端 → 存储cookie
3. 后续请求 → 携带token → 网关验证 → 访问资源
"""

🛠️ 测试工具推荐

黑盒测试工具

  • 功能测试:Selenium, Cypress, Playwright
  • API测试:Postman, Insomnia, REST Assured
  • 性能测试:JMeter, LoadRunner, Gatling
  • 安全测试:OWASP ZAP, Burp Suite

白盒测试工具

  • 单元测试:JUnit, pytest, Mocha, Jest
  • 代码覆盖率:JaCoCo, Istanbul, coverage.py
  • 静态分析:SonarQube, ESLint, Pylint
  • 调试工具:Chrome DevTools, PyCharm Debugger

串测/集成测试工具

  • API集成:Postman Collections, Karate DSL
  • 端到端:Cypress, TestCafe, Selenium Grid
  • 微服务测试:Pact, Mountebank, WireMock
  • CI/CD集成:Jenkins, GitLab CI, GitHub Actions

📈 测试最佳实践

1. 测试策略制定

项目阶段: 开发初期
重点测试:
- 白盒测试: 70% (单元测试、代码审查)
- 黑盒测试: 20% (核心功能验证)
- 串测: 10% (主要流程验证)

项目阶段: 测试阶段
重点测试:
- 黑盒测试: 50% (功能完整测试)
- 串测: 30% (集成流程测试)
- 白盒测试: 20% (重点模块深度测试)

项目阶段: 上线前
重点测试:
- 串测: 50% (端到端回归测试)
- 黑盒测试: 30% (用户场景测试)
- 白盒测试: 20% (关键路径测试)

2. 测试用例设计原则

  • 黑盒测试:基于需求,覆盖所有功能点
  • 白盒测试:基于代码,覆盖所有逻辑路径
  • 串测:基于业务流程,覆盖主要用户场景

3. 测试自动化策略

# 自动化测试金字塔
test_automation = {
"unit_tests": { # 白盒测试
"coverage": ">80%",
"tools": ["pytest", "JUnit"],
"execution": "每次提交"
},
"integration_tests": { # 串测
"coverage": "核心流程",
"tools": ["Postman", "Cypress"],
"execution": "每日构建"
},
"e2e_tests": { # 黑盒测试
"coverage": "关键用户旅程",
"tools": ["Selenium", "Playwright"],
"execution": "发布前"
}
}

🚀 实际工作建议

对于测试工程师

  1. 掌握黑盒测试技术:等价类、边界值、场景法等
  2. 学习自动化工具:Selenium、Postman、JMeter
  3. 理解业务流程:能够设计端到端测试用例
  4. 了解白盒基础:能看懂代码,与开发更好协作

对于开发工程师

  1. 重视白盒测试:编写高质量单元测试
  2. 参与代码审查:提前发现代码问题
  3. 理解黑盒思维:从用户角度思考问题
  4. 关注集成测试:确保模块间协作正常

对于团队

  1. 建立测试金字塔:合理分配测试资源
  2. 实施持续集成:自动化执行测试
  3. 定期回归测试:确保功能稳定
  4. 测试左移:在开发早期介入测试

💡 关键要点总结

  1. 黑盒测试外部视角,验证功能是否符合需求
  2. 白盒测试内部视角,验证代码逻辑是否正确
  3. 串测流程视角,验证系统间协作是否顺畅
  4. 三者互补而非互斥,应结合使用
  5. 遵循测试金字塔原则,基础测试要扎实
  6. 自动化是提高测试效率的关键

在实际项目中,通常需要混合使用这些测试方法:开发阶段以白盒测试为主,测试阶段以黑盒和串测为主,形成完整的质量保障体系。

端到端(E2E)测试

· 阅读需 1 分钟
Quany
软件工程师

通过AI写脚本

主要全量测试,集成测试,逐步提高 AI 编写测试;

通过测试用列编写脚本

  • 提示语:

/opsx-explore 硬件履约 e2e 测试脚本编写 请按照这个@硬件履约_测试用例1.1.md ,编写e2e 测试脚本,编写技术请参考: https://playwright.net.cn/docs e2e 测试脚本编写路径:yunfun-ui-admin-vben/apps/web-antd/e2e

在需求中分析测试用例(TDD)

  • 提示语:

/opsx-explore 硬件履约 @docs/订单管理/硬件履约需求设计文档.md 编写技术请参考: https://playwright.net.cn/docs,e2e 测试脚本编写路径:yunfun-ui-admin-vben/apps/web-antd/e2e

用他们设计的智能体编写测试脚本 (效果不好)

https://playwright.net.cn/docs/test-agents

定时任务

每天晚上跑全量测试脚本,如果有 bug,先 AI 修复一版,再让人工确认后提交;

报告可以推送到前端公共库;或者 netlify;

人工生成

主要为了复现 bug,精细化测试,补充全量测试脚本;

文档:https://playwright.net.cn/docs/codegen-intro 作业目录:e2e

计算资源整合设计

· 阅读需 11 分钟
Quany
软件工程师

这是一个非常完整的个人计算资源整合方案。我来帮你系统性地梳理和优化这个架构,并提供具体的实施配置。

🏗️ 整体架构图

┌─────────────────────────────────────────────────────────┐
│ 控制层 (OpenClaw) │
│ iPhone 12mini / 13 / 小米13 │
└─────────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 办公网 │ │ 家庭网 │ │ 移动办公 │
│ Mac mini │◄─┤ NUC Mint │ │ MacBook Pro │
│ (工作主力) │ │ (服务器/DB) │ │ (探索学习) │
└──────┬──────┘ └──────┬──────┘ └─────────────┘
│ │
└───────┬────────┘

┌─────────────┐
│ Tailscale │
│ 虚拟局域网 │
└──────┬──────┘

┌─────────────┼─────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌──────────┐
│ GitLab │ │ CSDM工时 │ │ Cloudflare│
│ 代码仓 │ │ 管理系统 │ │ 内网穿透 │
└────────┘ └──────────┘ └──────────┘

🖥️ 设备角色优化建议

1. NUC Linux Mint - 核心服务器

角色定位: 家庭数据中心 + 备份中枢 + 开发测试环境
建议服务:
- Docker 容器编排 (Portainer 管理)
- PostgreSQL / MySQL 主数据库
- Redis 缓存 + 消息队列
- MinIO 对象存储 (S3兼容)
- Gitea 轻量代码托管 (GitLab镜像)
- Syncthing 文件同步核心节点
- 归档硬盘挂载点 (/mnt/archive)

2. Mac mini - 工作终端

角色定位: 开发主力机 + 办公网关
建议配置:
- 开发环境: VS Code / Cursor / Xcode
- 本地Docker Desktop (轻量容器)
- 连接到NUC的远程数据库/Redis
- Tailscale subnet router (如需访问公司网段)

3. MacBook Pro 2015 - 移动/实验环境

角色定位: 学习探索 + 应急办公 + 备用终端
建议用途:
- 新框架/语言实验 (避免污染主力机)
- 外出办公轻量使用
- 作为备用节点加入Tailscale

🔧 具体配置实施

Tailscale 网络配置

# 在所有设备安装后,设置标签和ACL
# 1. NUC 作为网络出口和文件服务器
sudo tailscale up --advertise-exit-node --advertise-routes=192.168.1.0/24

# 2. Mac mini 开启远程访问
sudo tailscale up --ssh

# 3. 启用 MagicDNS 和 HTTPS证书
# 在 Tailscale Admin Console 开启:
# - Magic DNS
# - HTTPS certificates
# - Tailscale SSH

ACL 配置示例 (huJSON 格式):

{
"acls": [
{"action": "accept", "src": ["group:mobile"], "dst": ["tag:server:*"]},
{"action": "accept", "src": ["tag:work"], "dst": ["tag:server:22,80,443,5432,6379"]},
{"action": "accept", "src": ["tag:work"], "dst": ["tag:work:*"]}
],
"tagOwners": {
"tag:server": ["autogroup:admin"],
"tag:work": ["autogroup:admin"]
}
}

开发环境国内化 - 完整配置

1. macOS (Mac mini & MacBook)

# Homebrew 清华镜像
export HOMEBREW_INSTALL_FROM_API=1
export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"

# 写入 ~/.zshrc
cat >> ~/.zshrc << 'EOF'
# Homebrew 国内镜像
export HOMEBREW_INSTALL_FROM_API=1
export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"

# Node.js 国内镜像
export NVM_NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node
export NODE_MIRROR=https://npmmirror.com/mirrors/node
export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
export ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/

# Python 国内镜像
export PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
export PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn

# Ruby 国内镜像
export GEM_SOURCES=https://gems.ruby-china.com

# CocoaPods 清华镜像
export CP_HOME_DIR="$HOME/.cocoapods"
export COCOAPODS_SPECS_GIT_URL=https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git

# Golang 国内代理
export GOPROXY=https://goproxy.cn,direct
export GOSUMDB=sum.golang.google.cn

# Rust 国内镜像
export RUSTUP_DIST_SERVER=https://rsproxy.cn
export RUSTUP_UPDATE_ROOT=https://rsproxy.cn/rustup
EOF

# npm 使用淘宝镜像
npm config set registry https://registry.npmmirror.com
npm install -g pnpm yarn
pnpm config set registry https://registry.npmmirror.com
yarn config set registry https://registry.npmmirror.com

# CocoaPods 设置清华镜像
pod repo remove master
pod repo add master https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git

2. NUC Linux Mint

# 1. 更换 APT 源为阿里云
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo tee /etc/apt/sources.list << 'EOF'
deb https://mirrors.aliyun.com/linuxmint zena main upstream import backport
deb https://mirrors.aliyun.com/ubuntu noble main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu noble-updates main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu noble-backports main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu noble-security main restricted universe multiverse
EOF

# 2. Docker 阿里云镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://<你的ID>.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
sudo systemctl restart docker

# 3. pip 清华镜像
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# 4. npm 国内镜像 (同 macOS)

# 5. Golang 代理
echo 'export GOPROXY=https://goproxy.cn,direct' >> ~/.bashrc

归档备份系统架构

┌─────────────────────────────────────────────────────────┐
│ 数据分层策略 │
├─────────────────────────────────────────────────────────┤
│ 热数据 (SSD) │ 温数据 (外置HDD) │ 冷数据 (云端) │
│ ─────────── │ ─────────────── │ ───────────── │
│ • 当前项目 │ • 历史项目归档 │ • GitHub Releases│
│ • 活跃数据库 │ • 大文件/媒体 │ • Gitee 仓库 │
│ • 常用文档 │ • 备份镜像 │ • Cloudflare R2 │
│ • 开发环境 │ • 日志文件 │ • 网盘 (阿里云) │
└─────────────────────────────────────────────────────────┘

NUC 归档服务器配置

# 1. 挂载归档硬盘 (假设 /dev/sdb1)
sudo mkdir -p /mnt/archive
sudo blkid /dev/sdb1 # 获取UUID
echo 'UUID=xxxxx /mnt/archive ext4 defaults,noatime 0 2' | sudo tee -a /etc/fstab
sudo mount -a

# 2. 目录结构规划
sudo mkdir -p /mnt/archive/{backups,projects,media,logs,databases}
sudo chown -R $USER:$USER /mnt/archive

# 3. 安装 restic (现代化备份工具)
sudo apt install restic

# 初始化备份仓库
restic init --repo /mnt/archive/backups/restic-repo
# 设置密码环境变量
export RESTIC_PASSWORD="your-secure-password"
echo 'export RESTIC_PASSWORD="your-secure-password"' >> ~/.bashrc

自动化备份脚本 (/opt/scripts/backup.sh)

#!/bin/bash
# 每日自动备份脚本

DATE=$(date +%Y%m%d)
LOG_FILE="/var/log/backup-$DATE.log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo "========== 备份开始: $(date) =========="

# 1. 数据库备份 (PostgreSQL示例)
echo ">>> 备份数据库..."
sudo -u postgres pg_dumpall | gzip > /mnt/archive/databases/postgres-full-$DATE.sql.gz
find /mnt/archive/databases -name "postgres-full-*.sql.gz" -mtime +30 -delete

# 2. Redis 持久化备份
echo ">>> 备份 Redis..."
redis-cli BGSAVE
sleep 5
cp /var/lib/redis/dump.rdb /mnt/archive/databases/redis-$DATE.rdb
find /mnt/archive/databases -name "redis-*.rdb" -mtime +30 -delete

# 3. 项目代码备份 (使用 restic)
echo ">>> 备份项目文件..."
restic -r /mnt/archive/backups/restic-repo backup \
/home/user/projects \
/home/user/documents \
--exclude-file=/opt/scripts/backup-exclude.txt \
--tag "daily,$DATE"

# 4. 清理旧备份 (保留最近30天,每月1号保留月度备份)
echo ">>> 清理旧备份..."
restic -r /mnt/archive/backups/restic-repo forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 12 \
--prune

# 5. 同步到云端 (Gitee/GitHub)
echo ">>> 同步到云端..."
cd /home/user/projects && ./sync-to-gitee.sh

echo "========== 备份完成: $(date) =========="

排除文件配置 (/opt/scripts/backup-exclude.txt)

# 依赖和缓存
node_modules
vendor
__pycache__
*.pyc
.pytest_cache
.mypy_cache
.ruff_cache

# 构建输出
dist
build
target
out
.next
.nuxt

# 日志和临时文件
*.log
*.tmp
.DS_Store
Thumbs.db

# 大型媒体文件 (单独处理)
*.mp4
*.mov
*.iso
*.dmg

# IDE
.idea
.vscode
*.swp
*.swo

Crontab 定时任务

# 编辑 crontab
crontab -e

# 每日凌晨2点执行备份
0 2 * * * /opt/scripts/backup.sh

# 每周日凌晨3点执行深度清理
0 3 * * 0 /opt/scripts/cleanup.sh

# 每小时同步代码到 Gitee
0 * * * * cd /home/user/projects && git push gitee main

文档搜索与知识管理

推荐方案:
方案A (轻量):
- 搜索: ripgrep + fzf (本地)
- 管理: Obsidian + Git同步

方案B (自托管):
- Meilisearch: 轻量级搜索引擎
- Outline: 团队Wiki (Docker部署)
- 索引 /mnt/archive/documents

方案C (混合):
- 本地: AnyTXT Searcher (全文检索)
- 云端: Notion / 飞书 (摘要同步)

Meilisearch 部署 (NUC Docker):

# docker-compose.yml
version: '3'
services:
meilisearch:
image: getmeili/meilisearch:latest
container_name: meilisearch
environment:
- MEILI_MASTER_KEY=your-master-key
- MEILI_NO_ANALYTICS=true
volumes:
- /mnt/archive/meili_data:/meili_data
ports:
- "7700:7700"
restart: unless-stopped

# 文档爬虫 (自动索引)
doc-indexer:
image: getmeili/docs-scraper:latest
environment:
- MEILISEARCH_HOST_URL=http://meilisearch:7700
- MEILISEARCH_API_KEY=your-master-key
volumes:
- /mnt/archive/documents:/docs:ro
depends_on:
- meilisearch

OpenClaw 远程控制集成

# 概念性架构:OpenClaw 控制端点设计
# 部署在 NUC 上的 FastAPI 服务,接收手机指令

from fastapi import FastAPI, HTTPException
import subprocess
import paramiko

app = FastAPI(title="OpenClaw Control API")

@app.post("/cmd/{device}")
async def remote_command(device: str, command: Command):
"""
设备控制端点:
- mac-mini: 工作机控制
- nuc: 服务器控制
- macbook: 备用机控制
"""
hosts = {
"mac-mini": "100.64.1.2",
"nuc": "127.0.0.1",
"macbook": "100.64.1.3"
}

if device not in hosts:
raise HTTPException(404, "Device not found")

# 通过 Tailscale IP 执行 SSH 命令
result = run_remote(hosts[device], command.script)
return {"status": "ok", "output": result}

@app.get("/status")
async def cluster_status():
"""获取所有设备状态"""
return {
"mac-mini": check_status("100.64.1.2"),
"nuc": get_local_status(),
"macbook": check_status("100.64.1.3"),
"storage": get_storage_stats()
}

@app.post("/backup/trigger")
async def trigger_backup():
"""手动触发备份"""
subprocess.run(["/opt/scripts/backup.sh"], check=True)
return {"message": "Backup started"}

Cloudflare 零成本部署方案

服务矩阵:
域名: autoclaude.cn

服务1 - 网站托管:
平台: Cloudflare Pages
源码: GitHub/GitLab -> 自动部署
用途: 个人主页 / 项目展示

服务2 - API/函数:
平台: Cloudflare Workers
用途: OpenClaw 公网入口 / Webhook接收

服务3 - 对象存储:
平台: Cloudflare R2 (10GB免费)
用途: 备份文件异地存储 / 图床

服务4 - 内网穿透:
平台: Cloudflare Tunnel
部署: NUC 上运行 cloudflared
用途: 无需公网IP访问家庭服务

服务5 - 邮件路由:
平台: Cloudflare Email Routing
用途: hi@autoclaude.cn 转发到个人邮箱

Cloudflare Tunnel 配置 (NUC):

# 安装 cloudflared
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# 登录并创建隧道
cloudflared tunnel login
cloudflared tunnel create home-nuc

# 配置 config.yml
mkdir -p ~/.cloudflared
cat > ~/.cloudflared/config.yml << EOF
tunnel: <your-tunnel-id>
credentials-file: /home/user/.cloudflared/<your-tunnel-id>.json

ingress:
- hostname: nuc.autoclaude.cn
service: http://localhost:8080
- hostname: files.autoclaude.cn
service: http://localhost:5244 # 文件管理器
- hostname: db.autoclaude.cn
service: http://localhost:8081 # 数据库管理
- service: http_status:404
EOF

# 安装为系统服务
sudo cloudflared service install
sudo systemctl start cloudflared

📱 移动端 OpenClaw 控制界面 (概念)

<!-- 简化版 PWA 控制面板,可部署到 Cloudflare Pages -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenClaw Control</title>
<style>
:root { --primary: #007AFF; --bg: #f5f5f7; }
body { font-family: -apple-system, sans-serif; margin: 0; background: var(--bg); }
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; padding: 16px; }
.card { background: white; border-radius: 16px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
.online { background: #34C759; } .offline { background: #FF3B30; }
button { width: 100%; padding: 12px; border: none; border-radius: 10px; background: var(--primary); color: white; margin-top: 10px; }
.metric { font-size: 24px; font-weight: 600; color: #1c1c1e; }
.label { font-size: 12px; color: #8e8e93; text-transform: uppercase; }
</style>
</head>
<body>
<div class="grid">
<!-- 设备状态卡片 -->
<div class="card">
<div class="label">Mac Mini</div>
<span class="status-dot online"></span>在线
<div style="margin-top:8px; font-size:13px; color:#666;">
Load: 1.58 | 内存: 12/16GB
</div>
<button onclick="cmd('mac-mini', 'sleep')">睡眠</button>
</div>

<div class="card">
<div class="label">NUC Server</div>
<span class="status-dot online"></span>运行中
<div style="margin-top:8px; font-size:13px; color:#666;">
CPU: 23% | 存储: 83%
</div>
<button onclick="cmd('nuc', 'backup')">立即备份</button>
</div>

<!-- 存储概览 -->
<div class="card" style="grid-column: span 2;">
<div class="label">归档存储</div>
<div style="display:flex; justify-content:space-between; margin-top:8px;">
<div>
<div class="metric">767GB</div>
<div style="font-size:12px;color:#8e8e93;">已用 / 931GB</div>
</div>
<div style="text-align:right;">
<div class="metric">12天</div>
<div style="font-size:12px;color:#8e8e93;">上次备份</div>
</div>
</div>
<div style="height:4px;background:#e5e5ea;border-radius:2px;margin-top:12px;">
<div style="width:83%;height:100%;background:var(--primary);border-radius:2px;"></div>
</div>
</div>

<!-- 快捷操作 -->
<div class="card" style="grid-column: span 2;">
<div class="label">快捷操作</div>
<div style="display:grid; grid-template-columns: repeat(3, 1fr); gap:8px; margin-top:12px;">
<button onclick="openApp('gitlab')">GitLab</button>
<button onclick="openApp('csdm')">工时系统</button>
<button onclick="openApp('files')">文件管理</button>
</div>
</div>
</div>

<script>
// 调用 Cloudflare Worker API
async function cmd(device, action) {
await fetch(`https://api.autoclaude.cn/cmd/${device}`, {
method: 'POST',
body: JSON.stringify({action})
});
}
</script>
</body>
</html>

🚀 实施优先级建议

阶段任务预计时间影响
P0Tailscale 组网 + 设备互通30分钟⭐⭐⭐ 基础网络
P0NUC Docker + 数据库部署2小时⭐⭐⭐ 核心服务
P1开发环境国内镜像配置1小时⭐⭐ 日常效率
P1归档硬盘挂载 + restic 备份3小时⭐⭐⭐ 数据安全
P2Cloudflare Tunnel 内网穿透1小时⭐⭐ 远程访问
P2自动化脚本 + Crontab2小时⭐⭐ 自动化
P3OpenClaw API + 手机端界面4小时⭐ 便利性
P3文档搜索系统部署3小时⭐ 知识管理

这个方案充分利用了你现有的硬件资源,通过 Tailscale 实现安全组网,以 NUC 作为数据中心核心,配合 Cloudflare 的免费服务实现零成本运维。需要我详细展开任何部分的配置细节吗?

详细地址组件

· 阅读需 2 分钟
Quany
软件工程师

配置说明

  • fieldName: 字段必须是字符串,会塞入 json 格式的内容;
  • 必须搭配国家/地区(customerCountry)使用;

使用说明

模态框交互的详细地址组件

  • 组件代码路径:yunfun-ui-admin-vben/apps/web-antd/src/views/crm/customer/components/DetailByAddressFormat.vue

form-data.ts/data.ts 配置如下:

    {
fieldName: 'detailByAddressFormat',
label: $t('web.crmCustomer.create.form.detailByAddressFormat', {
defaultValue: '地址格式的地址',
}),
component: 'DetailByAddressFormat',
componentProps: {
countryCode: undefined,
},
dependencies: {
triggerFields: ['customerCountry'],
show: (values: any) => !!(values.customerCountry ?? '').trim(),
componentProps: (values: any) => ({
countryCode: (values.customerCountry ?? '').trim() || undefined,
}),
rules: () => z.string().optional(),
},
defaultValue: undefined,
rules: z.string().optional(),
formItemClass: 'col-span-2',
}

平铺详细地址组件

  • 组件代码路径:yunfun-ui-admin-vben/apps/web-antd/src/views/crm/customer/components/DetailByAddressFormatFlat.vue

form-data.ts/data.ts 配置如下:

  {
fieldName: 'detailByAddressFormatFlat',
component: 'DetailByAddressFormatFlat',
componentProps: {
countryCode: undefined,
},
dependencies: {
triggerFields: ['customerCountry'],
show: (values: any) => !!(values.customerCountry ?? '').trim(),
componentProps: (values: any) => ({
countryCode: (values.customerCountry ?? '').trim() || undefined,
}),
rules: () => z.string().optional(),
},
labelWidth: 0, // 这样可以对齐排版
defaultValue: undefined,
rules: z.string().optional(),
formItemClass: 'col-span-2',
},

相关解析函数

function parseDetailByAddressFormatValue(raw: unknown): null | {
components?: {
adminDiv?: string;
city?: string;
postalCode?: string;
};
fullText?: string;
} {
if (typeof raw !== 'string' || !raw.trim()) return null;
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object') return null;
return parsed as {
components?: { adminDiv?: string; city?: string; postalCode?: string };
fullText?: string;
};
} catch {
return null;
}
}

function mergeDetailedAddressMl(
fullText: string | undefined,
currentMl: Record<string, string>,
) {
const next = { ...(currentMl || {}) } as Record<string, string>;
const zh = (fullText ?? '').trim();
if (zh) {
next['zh-CN'] = zh;
}
return next;
}

演示说明

行政区联动组件

· 阅读需 2 分钟
Quany
软件工程师

表单使用

  • 必须跟着一个国家选择器
  • 联动规则:
    • 当国家选择器值变化时,联动组件的 label 值会变化(如果接口有返回该国家的行政区名称的话),如果没有返回则显示默认文案「行政区」。
    • 当国家选择器有值时,联动组件必填;当国家选择器无值时,联动组件非必填。
  • 联动组件的 options 由接口返回,接口参数为国家选择器的值,接口返回的 options 中包含 label 和 value 两个字段。
  • 联动组件的 label 默认文案为「行政区」,可自定义文案。

{
fieldName: 'customerCountry',
label: $t('web.crmCustomer.create.form.customerCountry', {
defaultValue: '注册地国家/地区',
}),
component: 'ApiSelect',
componentProps: {
api: async () => {
const countries = await getSimpleCountryList();
return (countries || []).map((c) => ({
label: `${c.text} (${c.countryCode})`,
value: c.countryCode,
}));
},
labelField: 'label',
valueField: 'value',
placeholder: $t(
'web.crmCustomer.create.form.placeholder.selectCustomerCountry',
{ defaultValue: '请选择注册地国家/地区' },
),
showSearch: true,
optionFilterProp: 'label',
getPopupContainer: getSelectPopupContainer,
},
defaultValue: undefined,
rules: z
.string({
required_error: $t(
'web.crmCustomer.create.form.validation.customerCountryRequired',
{ defaultValue: '注册地国家/地区不能为空' },
),
invalid_type_error: $t(
'web.crmCustomer.create.form.validation.customerCountryRequired',
{ defaultValue: '注册地国家/地区不能为空' },
),
})
.trim()
.min(
1,
$t('web.crmCustomer.create.form.validation.customerCountryRequired', {
defaultValue: '注册地国家/地区不能为空',
}),
),
formItemClass: 'col-span-1',
},
{
fieldName: 'adminDiv',
label: getAdminDivDefaultName(),
component: 'AdminDivSelector',
componentProps: {
countryCode: undefined,
},
dependencies: {
triggerFields: ['customerCountry'],
show: (values: any) => !!(values.customerCountry ?? '').trim(),
trigger: async (values: any, actions: any) => {
const countryCode = (values?.customerCountry ?? '').trim();
const locale = preferences.app.locale || 'zh-CN';
const nextLabel = countryCode
? (
await getAdminDivDisplayName({
countryCode,
locale,
})
)?.displayName || getAdminDivDefaultName()
: getAdminDivDefaultName();

const currentSchema = actions?.state?.schema ?? [];
const currentLabel = currentSchema.find(
(item: any) => item?.fieldName === 'adminDiv',
)?.label;

// 仅在文案变化时更新,避免重复 setState 触发联动抖动
if (
typeof currentLabel === 'string' &&
currentLabel.trim() === nextLabel.trim()
) {
return;
}

actions?.updateSchema?.([
{
fieldName: 'adminDiv',
label: nextLabel,
},
]);
},
componentProps: (values: any) => ({
countryCode: (values.customerCountry ?? '').trim() || undefined,
}),
rules: (values: any) => {
// 提交时在 create.vue 中做显式校验,避免国家切换时即时触发必填报错
return z.string().optional();
},
},
defaultValue: undefined,
rules: z.string().optional(),
formItemClass: 'col-span-1',
},

表单详情使用

行政区的 code,拼装好传到接口,后端去查,然后把显示名称组织好返回前端 ,需要加个这样的接口,根据请求的语言,返回对应的内容

技能包管理模块与生成器 Skill 设计

· 阅读需 4 分钟
Quany
软件工程师

我来搜索关于 OpenSpec 和 Superpowers 的相关信息。Superpowers 可能指的是 Superpowers.js(JavaScript 解析器库)或其他相关技术。 这两个项目代表了 AI 辅助编程领域的两种截然不同的技术路径,它们并非竞品关系,而是分层协作的"兄弟俩"

核心定位对比

维度OpenSpecSuperpowers
本质变更治理层(Change-centric)执行质量层(Behavior-centric)
核心目标对齐需求、沉淀规范、追溯变更拧紧执行过程、保障代码质量、强制纪律
解决痛点"变更一开始就走歪""方向对了,但执行做烂"
工作方式工件驱动(proposal/specs/design/tasks)行为驱动(skills + workflow)
关键产出可审计的规范文档 + 增量变更历史高质量代码 + 强制验证流程

OpenSpec:规范驱动的变更治理

由 Fission AI 开发,核心理念是**"先写规范,再写代码"**,通过 Delta 机制管理增量变更 。

核心特性:

  • 棕地优先(Brownfield-first):专为改造现有代码库设计,分离 openspec/specs/(当前事实)和 openspec/changes/(提案变更)
  • Delta 规范:用 ADDED/MODIFIED/REMOVED 标记追踪增量,而非重写全部规范
  • 四阶段工作流:Draft Proposal → Review → Apply → Archive
  • 多工具支持:兼容 Claude Code、Cursor、GitHub Copilot 等 20+ 种 AI 工具
  • 零依赖:MIT 开源,无需 API 密钥

适用场景:需求复杂、边界不清的老项目改造;需要长期维护、严格架构规范、强调团队协作的大型项目 。


Superpowers:流程强制的执行质量保障

由 Obra 开发,核心理念是**"流程即法律"**,通过不可跳过的"技能"约束 AI 行为,强制遵循 TDD、Code Review 等最佳实践 。

核心特性:

  • 强制技能调用brainstormingwriting-planstest-driven-developmentcode-reviewfinishing-a-development-branch
  • TDD 强制:必须遵循 Red-Green-Refactor 循环,即使是修小 Bug 也会触发全套流程
  • 任务细化与并发:自动拆分任务粒度,决定哪些适合并行推进
  • 系统化调试systematic-debugging 技能避免"胡试"式调 Bug
  • 验证与收尾纪律verification-before-completion 确保代码真正跑过验证

适用场景:从 0 到 1 的需求探索;对代码质量、测试覆盖有极高要求的项目;需要养成良好编码习惯的新手 。


关键差异:不是竞品,是分层互补

对比点OpenSpecSuperpowers
计划层级变更级规划(Why & What)执行级规划(How & When)
核心问题这次变更为什么要做?到底要改什么?任务拆得够不够细?有没有先写测试?
强项规格驱动、变更沉淀、影响可追溯任务细化、TDD、验证、review、收尾纪律
盲区不强制执行代码质量不天然沉淀变更工件与长期规格资产
最大优势适合老项目增量治理强制纪律避免执行偷懒

一句话总结

OpenSpec 负责"写清楚",Superpowers 负责"做干净"


组合使用:企业级 AI 编码工作流

真正复杂的项目往往需要两者配合 :

需求与变更对齐      → OpenSpec(/opsx:new → /opsx:ff)
执行质量控制 → Superpowers(brainstorming → TDD → review)
团队协作与长期沉淀 → OpenSpec + Superpowers 共同补位

典型组合场景

  1. 老项目重构:先用 OpenSpec 对齐 change 的范围和设计,再用 Superpowers 保证实施质量
  2. 中大型功能上线:OpenSpec 保证需求清楚,Superpowers 保证测试、验证不掉链子
  3. 又怕跑偏又怕返工:OpenSpec 稳住方向,Superpowers 稳住执行

选择建议

你的现状推荐工具
最缺"需求对齐、变更沉淀、团队协作规范"OpenSpec
最缺"TDD、调试方法、验证纪律"Superpowers
需求明确、追求快速验证OpenSpec
需求模糊、需要探索性开发Superpowers
已把 AI 真正用进工作项目两者组合

正如社区开发者所言:它们不是"谁更好"的关系,而是**"变更治理"与"执行质量"两个维度的互补** 。

技能包管理模块与生成器 Skill 设计

· 阅读需 8 分钟
Quany
软件工程师

日期:2025-02-09
状态:已确认


第一节:目标与范围

目标

  • 系统侧:在现有芋道后台中增加「技能包」管理能力,运营/管理员可创建、编辑、发布技能包(业务数据落库,供 AI Agent 或其它业务消费)。
  • 开发侧:在 Cursor 中提供一个「创建技能包(管理模块)」的 skill,能像 biz-crud-create-page 一样一键生成「技能包」的整条管理链路(表结构 + 字典 + 菜单 + 后端 CRUD + 前端列表/表单/详情),并遵循项目既有规范。

范围

  • 技能包在系统内视为一种业务实体:有名称、编码、描述、状态(草稿/已发布等)、版本、所属分类等可配置字段;具体字段在第二节数据模型中确定。
  • 管理能力:列表(含筛选)、新增、编辑、删除、发布/下架;详情页可展示包内「技能」列表或配置摘要(首期做简单列表,复杂编排可后期扩展)。
  • 生成器 skill:只负责生成「技能包管理」这条 CRUD 链路(含一个技能包表及必要字典/菜单),不生成「包内单个 skill 的 SKILL.md 文件」;若以后需要「从管理页导出为 Cursor skill 目录」,再单独做导出功能。
  • 消费方式本期只做数据就绪:表与 API 先有,供前端或内部调用;若已有或计划有 Agent 服务,可直接读同一套 API。

不包含(YAGNI)

  • 包内技能的在线编辑器、版本 diff、审批流、多租户隔离(除非现有项目已强依赖租户)。
  • 技能包市场、分享、权限到「包内单条技能」的细粒度控制。

成功标准

  • 用 skill 生成一遍后,能在后台看到技能包菜单,完成列表/增删改/发布状态切换。
  • 生成物符合现有数据库、后端、前端规则(与 lead/source 或 lead/personal 风格一致)。

第二节:数据模型与字典

主表 ai_skill_package

  • 与现有规范一致:主键 id(雪花)、creator/create_time/updater/update_time/deletedtenant_id(多租户)。
  • 业务字段:name(名称)、code(编码,租户内唯一,用于 API/导出)、description(描述,VARCHAR(500) 或 TEXT)、status(状态,见下)、sort(排序,INT 默认 0)、content(TEXT,可选,存包内技能摘要或 JSON,后续可扩展)。
  • 索引:uk_ai_skill_package_code_tenant (code, tenant_id, deleted)idx_ai_skill_package_status (status)idx_ai_skill_package_sort (sort)

字典

  • skill_package_status:草稿(DRAFT)、已发布(PUBLISHED)、已下架(OFFLINE)。列表筛选、详情、发布/下架操作均用该字典。
  • (可选)skill_package_category:如「业务生成」「Agent 技能」等,便于后续分类筛选;若首期不做分类,可不建该字典。

包内技能(首期从简)

  • 不单独建「技能项」表,用主表 content 存一段文本或 JSON 即可(例如技能名称列表 + 简要说明),详情页只读展示。若后续需要逐条增删改「包内技能」,再增加 ai_skill_package_item 表与前后端。

菜单与权限

  • 一级菜单(示例):「AI 技能」或放在现有某父级下,path 如 /ai/skill-package
  • 二级:技能包管理,permission 前缀 ai:skill-package:query/create/update/delete,并可加 ai:skill-package:publish 控制发布/下架。

第三节:后端与前端实现要点

后端

  • 模块:放在现有 yudao-module-ai,若当前只有 pom,则在该模块下按规范建包:controller.admindal.dataobject.skilldal.mysql.skillservice.skillconvert.skillcontroller.admin.vo.skill(PageReqVO/RespVO/SaveReqVO,按需 SimpleRespVO)。
  • 接口约定:Base URL /admin-api/ai/skill-package。提供:分页查询 getPage、详情 get、新增 create、修改 update、删除 delete、发布 publish、下架 offline。权限标识:ai:skill-package:query/create/update/delete,发布/下架可用 ai:skill-package:publish 或复用 update。
  • 实现要点:DO 继承 TenantBaseDO,表名 ai_skill_package。发布/下架即把 status 置为 PUBLISHED/OFFLINE 并做幂等与状态校验(如仅草稿可发布、仅已发布可下架)。错误码在 yudao-module-ai 的 ErrorCodeConstants 中新增,Controller 用 @PreAuthorize 与现有风格一致。

前端

  • 路由:在 router/routes/modules 下新增 ai.ts(或并入已有 AI 路由),path 如 /ai/skill-package,组件 ai/skill-package/index;列表、新增/编辑、详情对应同一模块下的 index、create、edit、detail。
  • 列表页Page + useVbenVxeGrid,查询项:名称(Input)、编码(Input)、状态(Select,DICT_TYPE.skill_package_status)。表格列:名称、编码、状态(CellDict)、排序、更新时间、操作(编辑/删除/发布/下架,按状态显隐)。API 与后端分页约定一致(pageNo、pageSize、表单条件、排序)。
  • 表单(新增/编辑):名称、编码、描述、排序、状态(草稿时可选)、content(可选,Textarea)。编码新增时必填,编辑时只读或禁用。校验:编码格式、名称非空。
  • 详情页:只读展示上述字段;若有 content,以只读区块或简单 JSON/文本展示「包内技能」摘要。
  • 发布/下架:列表操作列按钮,调用 publish/offline 接口后刷新列表;可加二次确认(如「确认发布?」)。

与现有风格对齐

  • 后端 VO 命名、分页参数、统一返回与 CRM 模块一致;前端 data.ts 中 formSchema/columns、TableAction、权限 ai:skill-package:xxx、i18n key 前缀统一(如 ai.skillPackage.xxx)。组件与数据源遵守 .cursor/skills/biz-crud-create-page/component-data-mapping.md(本业务若无特殊下拉可仅用字典)。

第四节:生成器 Skill 设计

定位与触发

  • 新建项目内 skill:.cursor/skills/create-skill-package-module/,内含 SKILL.md。触发场景:用户说「生成技能包管理模块」「创建技能包管理页」「按 createPage 风格做技能包」等时,Agent 优先使用该 skill。
  • biz-crud-create-page 的关系:不替代,复用其规则与参考。本 skill 只针对「技能包」这一固定业务,产出表名、实体名、模块名、菜单/权限前缀、字典类型等均写死在 skill 内,无需用户再走「查询项→表格→功能清单」多步交互;若以后要支持「自定义实体名的技能包」,再考虑引入类似 create-page 的交互。

生成顺序(与 create-page 一致,严禁颠倒)

  1. SQLsql/mysql/ 下新增 ai_skill_package.sql(建表)、ai_skill_package_dict.sql(skill_package_status 等字典,幂等)、ai_skill_package_menu.sql(AI 技能 / 技能包管理菜单及 ai:skill-package:query 等按钮权限,含 role_id=1 的 system_role_menu)。
  2. 后端:在 yudao-module-ai 中生成 DO(TenantBaseDO)→ Mapper → Service 接口与 Impl → Controller(/admin-api/ai/skill-package)→ VO(PageReqVO、RespVO、SaveReqVO);ErrorCodeConstants 中新增错误码;发布/下架在 Service 内做状态流转与校验。
  3. 前端router/routes/modules/ai.ts 中注册列表与 detail/create/edit 路由;views/ai/skill-package/ 下 index.vue、data.ts、create.vue、edit.vue、detail.vue(弹窗 CRUD 则用 modules/form.vue);api/ai/skill-package/index.ts;i18n 键与菜单名称一致。

Skill 文档必须写清的内容

  • 本模块固定参数表:表名 ai_skill_package、模块名 ai、实体名 skillPackage(前端)、权限前缀 ai:skill-package、字典类型 skill_package_status、参考实现路径(可与本设计文档互相引用)。
  • 必读规则:.cursor/rules/backend/database.mdcbackend-project-structure.mdc.cursor/rules/frontend/biz-crud-page-standard.mdc;参考实现指向「技能包」生成后的代码或现有 lead/source 的目录结构说明。
  • 生成后校验:要求执行 node .cursor/skills/biz-crud-create-page/scripts/validate-page.mjs ai skill_package --backend(若 validate-page 支持 ai 模块);或在本 skill 下提供专用 validate-skill-package.mjs,检查 SQL、菜单、路由、views、API、后端 DO/Controller 是否存在且符合约定。

可选

  • 本 skill 的 reference.md 中列出「技能包管理」字段与接口清单,便于 Agent 一次生成完整;scripts/ 下仅保留校验脚本,不提供通用代码生成引擎。

第五节:错误处理与验收

错误处理

  • 后端:编码重复返回 400 及明确错误码;发布/下架状态不合法(如对已下架再下架)返回 400 并提示当前状态;删除前可做关联校验(若未来有包内技能表则先判空)。
  • 前端:接口失败用项目统一提示;列表加载失败可重试或空状态提示。
  • 生成器:校验脚本以非 0 退出并打印缺失项(如「缺少 ai_skill_package_menu.sql」),Agent 按提示补全后重新运行直至通过。

验收标准

  • 执行 skill 生成一遍后:执行 SQL、启动后端与前端,能在「AI 技能」下看到「技能包管理」,完成列表筛选、新增、编辑、删除、发布、下架、详情查看;数据落库且状态正确。
  • 生成代码通过项目既有 Lint/编译;菜单与权限与设计一致。