@adobe/spacecat-shared-data-access

中文文档
4.41.0发布于 2026/10/08Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 6056

Spacecat服务共享模块 - 数据访问

中文使用文档
文档目录44 节
  1. 安装
  2. 你得到的东西
  3. 快速启动
  4. 配置
  5. createDataAccess 配置
  6. 自定义 PostgREST 客户端
  7. 包装使用
  8. 直接 PostREST 查询
  9. URL 索引
  10. 语义索引
  11. 实地绘图行为
  12. 实体
  13. 现场投递
  14. 站点朗姆酒
  15. 配置模型方法
  16. 建筑
  17. V3 行为说明
  18. 正在变化的实体
  19. 1. 数据库计划 - 神秘数据服务
  20. 2. 模型/收集层——本套件
  21. 3. 整合测试全叠
  22. 从 V2 移动
  23. 什么还是一样
  24. 什么变化
  25. 需要的环境/配置更新
  26. 发展
  27. 地方发展
  28. 第一次设置
  29. 日常工作流程
  30. 常见命令(来自 packages/spacecat-shared-data-access (中文(简体) )
  31. 运行单元测试
  32. 用调试器运行单元测试
  33. 运行集成测试
  34. 运行线
  35. 自动补丁问题
  36. 清洁本地安装文物
  37. 整合测试
  38. 先决条件
  39. IT 带使用的默认图像
  40. 认证 Docker 到 ECR
  41. 运行
  42. 有用的覆盖
  43. 类型脚本
  44. 许可证

空间猫共享数据访问

@adobe/spacecat-shared-data-access 是Spacecat服务使用的共享数据访问层.

  • 主数据存储器 : 通过 PostgREST 发送邮件 (单位:千美元)@supabase/postgrest-js(中文(简体) )
  • 可选二级数据库: 第3条 (计: Configuration(中文(简体) )

安装

npm install @adobe/spacecat-shared-data-access

你得到的东西

该套件提供:

  • createDataAccess(config, log?, client?) ——返回实体收藏+ services.postgrestClient
  • dataAccessWrapper(fn) (默认导出) Helix/ Lambda 样式处理器
  • 外部API形状稳定的服务实体收藏/模型
  • services.postgrestClient 对非实体表格的PostgREST查询

快速启动

import { createDataAccess } from '@adobe/spacecat-shared-data-access';

const dataAccess = createDataAccess({
  postgrestUrl: process.env.POSTGREST_URL,
  postgrestSchema: process.env.POSTGREST_SCHEMA || 'public',
  postgrestApiKey: process.env.POSTGREST_API_KEY,
  // Only needed if you use Configuration entity:
  s3Bucket: process.env.S3_CONFIG_BUCKET,
  region: process.env.AWS_REGION,
}, console);

const site = await dataAccess.Site.findById('0983c6da-0dee-45cc-b897-3f1fed6b460b');
console.log(site?.getBaseURL());

配置

createDataAccess 配置

  • postgrestUrl (需要): PostgREST 服务器的基础 URL
  • postgrestSchema (可选) Postgres schema 由 PostgREST 曝光, 默认 public
  • postgrestApiKey (可选) : 添加为 apikey 财务报告和审定财务报表 Authorization: Bearer ...
  • postgrestHeaders (可选): PostgREST 客户端的额外信头
  • s3Bucket (可选) : 仅限 Configuration 实体
  • region (可选):S3客户端的 AWS 区域

自定义 PostgREST 客户端

您可以输入已经构建的 PostgREST 客户端作为第三个参数 :

import { PostgrestClient } from '@supabase/postgrest-js';
import { createDataAccess } from '@adobe/spacecat-shared-data-access';

const client = new PostgrestClient(process.env.POSTGREST_URL, { schema: 'public' });
const dataAccess = createDataAccess({ postgrestUrl: process.env.POSTGREST_URL }, console, client);

包装使用

默认导出是附加的包件 context.dataAccess。 。 。 .

import wrap from '@adobe/helix-shared-wrap';
import dataAccessWrapper from '@adobe/spacecat-shared-data-access';

async function run(request, context) {
  const { dataAccess } = context;
  const site = await dataAccess.Site.findById(request.params.siteId);
  return {
    statusCode: site ? 200 : 404,
    body: site ? site.toJSON() : { error: 'not found' },
  };
}

export const main = wrap(run)
  .with(dataAccessWrapper);

包装从 context.env数字 :

  • POSTGREST_URL (默认倒计时 : http://localhost:3000(中文(简体) )
  • POSTGREST_SCHEMA
  • POSTGREST_API_KEY
  • S3_CONFIG_BUCKET
  • AWS_REGION

直接 PostREST 查询

用于查询不作为实体建模的 PostgREST 表格(例如分析视图、报告表格) postgrestClient 可在 dataAccess.services数字 :

const { Site } = dataAccess;                             // entity collections
const { postgrestClient } = dataAccess.services;         // raw PostgREST client

// Use entity collections as usual
const site = await Site.findById(siteId);

// Direct queries against non-entity tables
const { data, error } = await postgrestClient
  .from('brand_presence_executions')
  .select('execution_date, visibility_score, sentiment')
  .eq('site_id', siteId)
  .gte('execution_date', '2025-01-01')
  .order('execution_date', { ascending: false })
  .limit(100);

这是相同的 @supabase/postgrest-js PostgrestClient 用于实体收藏的内部实例。 查询构建器链可获取完整的 IDE 自动完成 .

URL 索引

源-URL指数表opportunity_urls, (中文(简体) ), suggestion_urls),不直接查询客户端——使用共享 syncUrlIndex 编号 : lookupEntityIdsByUrl 帮助器( 从软件包根导出) 因此, 写入和读取共享一个卡通化和一个存储路径 :

import {
  syncUrlIndex, syncUrlIndexMany, lookupEntityIdsByUrl,
} from '@adobe/spacecat-shared-data-access';

const { postgrestClient } = dataAccess.services;

// writer (needs the postgrest_writer role): full-replace an entity's source URLs
await syncUrlIndex(postgrestClient, {
  table: 'opportunity_urls', siteId, entityId, entityType, urls,
});

// writer, batched: sync many entities in one table without the per-entity fan-out
await syncUrlIndexMany(postgrestClient, {
  table: 'suggestion_urls', siteId, entityType, entries: [{ entityId, urls }],
});

// reader: which opportunities/suggestions are backed by these URLs?
const matches = await lookupEntityIdsByUrl(postgrestClient, {
  table: 'opportunity_urls', siteId, urls,
});

见包裹 CLAUDE.md ("URL Index"),用于舟曲化,单笔写作,和站点拓扑合同.

语义索引

矢量指数(opportunity_semantic_embedding, (中文(简体) ), suggestion_semantic_embedding)和全局查询-嵌入缓存(semantic_query_embedding),使用共享的帮助器,所以编剧和读者共享一个文本正常化,一个嵌入的一代和一个存储路径. 帮助者使用嵌入客户端 createEmbeddings(texts) (例如:. AzureEmbeddingClient 从 @adobe/spacecat-shared-gpt-client并进行嵌入本身.

import {
  indexSemanticTopics, indexSemanticClaims, embedQueries,
  lookupOpportunitiesByTopic, lookupSuggestionsByTopic, lookupSuggestionsByClaim,
  SEMANTIC_TARGETS,
} from '@adobe/spacecat-shared-data-access';

const { postgrestClient } = dataAccess.services;

// writer (needs the postgrest_writer role): full-replace each (entity, matchFieldType) topic group
// in one call. Unchanged texts are kept; a new text reuses a vector already stored in the same
// table for the site, then the query cache, and is embedded otherwise (in batches); stale texts
// are deleted; fields not listed are left alone. texts: [] clears a field.
const result = await indexSemanticTopics(postgrestClient, embeddingClient, {
  target: SEMANTIC_TARGETS.OPPORTUNITY, // or SEMANTIC_TARGETS.SUGGESTION
  siteId,
  entities: [{
    entityId, entityType: 'cited-analysis',
    fields: [{ matchFieldType: 'topic', texts: ['running shoes'] }],
  }],
});
// result: { entities: [{ entityId, fields: [{ matchFieldType, rejected, inserted, deleted, unchanged, ... }] }],
//           embedded, indexHits, cacheHits, indexError?, cacheError? }

// claim writer: same parameters and result, stored under the claim match type.
await indexSemanticClaims(postgrestClient, embeddingClient, {
  target: SEMANTIC_TARGETS.SUGGESTION,
  siteId,
  entities: [{
    entityId: suggestionId, entityType: 'cited-analysis',
    fields: [
      { matchFieldType: 'title', texts: ['Publish a pricing comparison page'] },
      { matchFieldType: 'topic', texts: ['running shoes'] },
    ],
  }],
});

// query side: validate (at most MAX_QUERY_TEXTS texts, each up to 2048 chars once normalized), read the
// cache (best-effort), embed the distinct misses in one call within a time budget.
// cacheWrites (touch hits + store misses) is best-effort; await it or hand it to waitUntil.
const { vectors, cacheWrites, cacheError } = await embedQueries(postgrestClient, embeddingClient, texts, { log });

// reader, batched: one best-first list of { entityId, entityType, score } per query vector.
// matchFieldTypes / entityTypes / statuses are optional filters (omitted or [] means no filter).
// statuses is applied before k, so filtered-out rows never take result slots.
// Searches run in the SEMANTIC_MATCHING_CONFIG.embedding generation, like the writer.
const opportunities = await lookupOpportunitiesByTopic(postgrestClient, {
  siteId, matchFieldTypes: ['topic'], entityTypes: ['cited-analysis'],
  statuses: ['NEW', 'IN_PROGRESS'], vectors, k: 10, minScore: 0.5,
});
// suggestions: entityTypes are parent opportunity types, statuses are Suggestion.STATUSES,
// opportunityStatuses filter on the parent opportunity.
const suggestions = await lookupSuggestionsByTopic(postgrestClient, {
  siteId, opportunityStatuses: ['NEW'], vectors,
});
// claim reader: same parameters, over the claim rows.
const claimMatches = await lookupSuggestionsByClaim(postgrestClient, {
  siteId, matchFieldTypes: ['title'], vectors,
});

匹配类型( N)topic, (中文(简体) ), claim)永远不是一个参数:每个维度都有自己的作家和读者(indexSemanticTopics 编号 : ...ByTopic, (中文(简体) ), indexSemanticClaims 编号 : ...ByClaim),设置 match_type 内部,所以一个话题搜索从不看到权利行,反之亦然. 尽管今天只有建议有权利主张读者. matchFieldType (文字来自哪个字段)和 entityType 是free-form,长度限制字符串:没有注册,所以一个生产者可以在没有共享发布的情况下索引一个新的字段或实体类型.

SEMANTIC_MATCHING_CONFIG.embedding (单位:千美元)model, (中文(简体) ), dims)是编剧和读者都使用的嵌入式一代;编剧, embedQueries 观察者从那里读出来 而不是把它当作参数 它是一个代码常数,而不是env配置,所以编剧和读者都同意,只要两者运行相同的数据访问版本;改变它意味着重新嵌入加升级两个消费者. 被再嵌入是自动的:编剧将另一代的行视为 Stale 并覆盖在实体的下一个刷新上.

编译器重新使用它已经存储在同一个表格中用于站点的向量(任何实体,匹配类型或字段,仅是当前世代),因此在其主题后索引一个建议的要求不会两次嵌入;它不会读取另一个目标表. 它读取了查询缓存, 但从未写; 只有 embedQueries 已经 低级缓存帮助器是内部的,因此任何外部的数据访问都无法写出作者和读者所信任的向量.

电话是内部分组的(分批256个,写作和搜索为20个组,id/hash读取和删除为50个组),因此呼叫者通过整个列表,而不是每个项目循环.

实地绘图行为

公共模型 API 仍为骆驼Case,而 Postgres/ PostgREST 表格则为蛇 case.

实例:

  • site.siteId < - > (中文(简体) ) sites.id
  • site.baseURL < - > (中文(简体) ) sites.base_url

绘图工作由基础PostgREST公用事业处理,并应用于读写路径.

实体

目前出口的实体包括:

  • ApiKey
  • AsyncJob
  • Audit
  • AuditUrl
  • Configuration
  • Entitlement
  • Experiment
  • FixEntity
  • FixEntitySuggestion
  • ImportJob
  • ImportUrl
  • KeyEvent
  • LatestAudit
  • Opportunity
  • Organization
  • PageCitability
  • PageIntent
  • Project
  • Report
  • ScrapeJob
  • ScrapeUrl
  • SentimentGuideline
  • SentimentTopic
  • Site
  • SiteCandidate
  • SiteEnrollment
  • SiteTopForm
  • SiteTopPage
  • Suggestion
  • TrialUser
  • TrialUserActivity

现场投递

这个 deliveryConfig object上的站点存储发送基础设施细节。 它是一个灵活的 JSON 对象(type: 'any'——未知财产被保留(未剥取).

财产类型说明
programId字符串云管理程序 ID
environmentId字符串云管理器环境 ID( 仅 AEM CS)
authorURL字符串AEM 作者实例 URL( 例如, ), https://author-p12345-e67890.adobeaemcloud.com(中文(简体) )
siteId字符串空间猫站点编号
imsOrgId字符串Adobe IMS 组织 客户身份识别
tenantId字符串云管理器租户标识符(来自 /program/{programId} 答复)
ipAllowlistExists布尔CM 程序是否配置了真正的 IP 允许列表( 不包括默认) 0.0.0.0/32 条目)
preferContentApi布尔是否为内容检索选择内容 API
contentSourcePath字符串网站的 AEM 内容根路径 。 曾分拆多个共享同名"云管理器"程序及环境的网站. 相应 /content/<site-name> 在 AEM 仓库中.

站点朗姆酒

这个 rumConfig 对象在 Site 跟踪 Real User Monitor (RUM) 域键可用性。 它被自动设定在网站创建上,并由每周更新 rum-config-refresh 审计主管.

财产类型说明
hasDomainKey布尔该网站的域是否注册了活动 RUM 域名密钥
lastCheckedAt字符串( ISO 8601)最近的 RUM 域名密钥检查的时间戳

配置模型方法

方法回返说明
getRumConfig(){ hasDomainKey, lastCheckedAt } | undefined返回当前朗姆花序,或 undefined 尚未设定的
hasRumDomainKey()boolean回返 true 如果一个 RUM 域名键激活, false 否则
updateRumConfig(hasDomainKey)void设定 hasDomainKey 和最新情况 lastCheckedAt 到当前时间

创建于 rum-config-refresh 处理器运行将没有 rumConfig 密钥 - 呼叫者必须治疗 undefined 作为"未知"而不是"没有设置".

建筑

Lambda / ECS service
  -> @adobe/spacecat-shared-data-access (this package)
       -> @supabase/postgrest-js
            -> mysticat-data-service (PostgREST + Aurora PostgreSQL)
                 https://github.com/adobe/mysticat-data-service

v2(已退休): ElectroDB - > DynamoDB (直接, schema- in-code) 电子数据库 - > DynamoDB v3(当前): PostgREST 客户端 - > 神秘数据服务 (数据库中)

数据库计划(表、索引、图表、赠款) 神秘数据服务 作为dbmate迁移. 这个软件包提供了JavaScript模型/收集层,将骆驼Case实体映射到蛇 case PostgREST API.

V3 行为说明

  • Configuration 在v3中仍为S3支持.
  • KeyEvent 在v3中被贬值,并故意放弃访问/模拟方法.
  • LatestAudit v3 中为虚拟,并衍生自 Audit 查询(不需要专用表格).

正在变化的实体

添加或修改一个实体现在需要更改 两个储存库数字 :

1. 数据库计划 - 神秘数据服务

创建用于计划变化的 dmate 迁移( 表格、 列、 索引、 赠款、 图表) :

# In mysticat-data-service
make migrate-new name=add_foo_column_to_sites
# Edit db/migrations/YYYYMMDDHHMMSS_add_foo_column_to_sites.sql
make migrate
docker compose -f docker/docker-compose.yml restart postgrest
make test

见 神秘数据服务 移民公约(所需赠款、指数、评论等).

2. 模型/收集层——本套件

更新实体计划、模型和/或收藏 src/models/<entity>/数字 :

文件改变什么
<entity>.schema.js添加/修改属性、参考文献、索引
<entity>.model.js添加业务逻辑方法
<entity>.collection.js添加自定义查询方法

添加新的属性示例 :

// In <entity>.schema.js, add to the SchemaBuilder chain:
.addAttribute('myNewField', {
  type: 'string',
  required: false,
  // Optional: custom DB column name (default: camelToSnake)
  // postgrestField: 'custom_column_name',
})

此自动生成 getMyNewField() 财务报告和审定财务报表 setMyNewField() 在模型上.

添加新实体 : 在任何现有的实体文件夹中按照模式创建 4 文件 :

  • <entity>.schema.js - SchemaBuilder定义
  • <entity>.model.js - 延伸 BaseModel
  • <entity>.collection.js - 延伸 BaseCollection
  • index.js ——再出口型号,收集型,计划型

然后在 src/models/index.js。 。 。 .

3. 整合测试全叠

# In this package — runs PostgREST in Docker
npm run test:it

综合测试 mysticat-data-service 来自ECR的 Docker 图像,所以新的 schema 更改必须首先作为新的图像标记(或针对本地 PostgREST 的测试)发布.

从 V2 移动

如果您正在从基于 DynamoDB/ ElectroDB 的 v2 升级 :

什么还是一样

  • 你还在用 createDataAccess(...)。 。 。 .
  • 您仍然通过 dataAccess.<Entity> (例如 dataAccess.Site) (中文(简体) ).
  • 模式/收集API旨在为服务呼叫者保持稳定.

什么变化

  • 后置商店现在通过PostgREST,而不是DynamoDB/ElectroDB.
  • 您必须提供 postgrestUrl (或 POSTGREST_URL (通过包装箱).
  • 变换时速 神秘数据服务 迁移,而不是代码.
  • Configuration 由S3支持(要求) s3Bucket编号 :S3_CONFIG_BUCKET (在使用时).
  • KeyEvent 在v3中贬值,现在丢出.
  • LatestAudit 不再是专用的表格,而是从 Audit 查询.

需要的环境/配置更新

  • 将旧的迪纳摩组合替换为:
    • POSTGREST_URL
    • 选项 POSTGREST_SCHEMA
    • 选项 POSTGREST_API_KEY
  • 仅在使用时保留 S3 配置 Configuration数字 :
    • S3_CONFIG_BUCKET
    • AWS_REGION

发展

地方发展

第一次设置

从单列波根:

npm install

可选: 从此工作空间校验软件包工具 :

cd packages/spacecat-shared-data-access
node -v
npm -v

日常工作流程

  1. 创建/ 切换到特性分支 .
  2. 在 src/ 试验 test/unit 财务报告和审定财务报表 test/it。 。 。 .
  3. 运行单元测试在延时.
  4. 在打开/合并公关前进行整合测试.
  5. 处理好问题.

常见命令(来自 packages/spacecat-shared-data-access(中文(简体) )

运行单元测试

npm test

用调试器运行单元测试

npm run test:debug

运行集成测试

npm run test:it

运行线

npm run lint

自动补丁问题

npm run lint:fix

清洁本地安装文物

npm run clean

整合套房 test/it 以 PostgREST 为主,并通过 Docker 运行.

整合测试

集成测试通过 Docker 编译并执行本地 Postgres + PostgREST 堆栈 摩卡套房在下面 test/it。 。 。 .

先决条件

  • Docker 桌面( 或者等同的 Docker 守护进程)
  • AWS CLI 配置了可以访问 Spacecat 开发 AWS 账户的证书 ECR寄存器( 仅在拉出默认的 ECR 图像时需要)

IT 带使用的默认图像

  • 仓库 : 682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service
  • 标记 : v1.11.0 (通过env var 管理)

认证 Docker 到 ECR

默认图像在 ECR repo 中 :

  • 空间猫发展(AWS3338)

如果你是第一次设置这个:

  1. 获取 AWS 证书 空间猫发展(AWS3338) 从 klam.corp.adobe.com。 。 。 .
  2. 把它们添加到 ~/.aws/credentials 在您选择的配置名称下.
  3. 在 ECR 登录命令中使用此配置文件 .

示例 ~/.aws/credentials 条目 :

[spacecat-dev]
aws_access_key_id = <your-access-key-id>
aws_secret_access_key = <your-secret-access-key>

仓库 :

  • 682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service

然后认证 Docker 到 ECR :

aws ecr get-login-password --profile spacecat-dev --region us-east-1 \
  | docker login --username AWS --password-stdin 682033462621.dkr.ecr.us-east-1.amazonaws.com

运行

npm run test:it

有用的覆盖

  • MYSTICAT_DATA_SERVICE_TAG: 覆盖图像标记( 推荐为版本的凸起)
  • MYSTICAT_DATA_SERVICE_REPOSITORY: 覆盖图像存储器
  • MYSTICAT_DATA_SERVICE_PLATFORM: 覆盖容器平台( 默认) linux/amd64(中文(简体) )
  • IT_POSTGREST_PORT: 覆盖已曝光的 PostgREST 端口( 默认) 3300(中文(简体) )
  • IT_POSTGRES_PORT: 覆盖已曝光的 Postgres 端口( 默认) 55432(中文(简体) )
export MYSTICAT_DATA_SERVICE_TAG=v1.11.0
export MYSTICAT_DATA_SERVICE_PLATFORM=linux/amd64
# optional if repository changes
export MYSTICAT_DATA_SERVICE_REPOSITORY=682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service

类型脚本

类型定义来自:

  • src/index.d.ts
  • src/service/index.d.ts —— 说 DataAccess 财务报告和审定财务报表 DataAccessServices 接口
  • src/models/**/index.d.ts ——实体收集和模型接口

这个 DataAccess 接口为所有实体集合提供完整的打字功能 services.postgrestClient (类型为: PostgrestClient 从 @supabase/postgrest-js) (中文(简体) ).

在 TS 项目中直接使用软件包;不需要额外的设置.

许可证

阿帕奇 - 2.0

版本与发布记录

@adobe/spacecat-shared-data-access 共发布 607 个版本,首次发布于 2023/12/01;过去 12 个月发布了 222 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.41.0latest2026/10/0830 天前
4.40.12026/10/0830 天前
4.40.02026/10/021 个月前
4.39.12026/10/021 个月前
4.39.02026/10/021 个月前
4.38.02026/09/301 个月前

关于 @adobe/spacecat-shared-data-access 的常见问题

@adobe/spacecat-shared-data-access 是什么?

Spacecat服务共享模块 - 数据访问

如何安装 @adobe/spacecat-shared-data-access?

在项目目录运行 npm install @adobe/spacecat-shared-data-access,或使用 pnpm add / yarn add / bun add。

@adobe/spacecat-shared-data-access 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@adobe/spacecat-shared-data-access 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@adobe/spacecat-shared-data-access 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.0.0 <25.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@adobe/spacecat-shared-data-access 可以商用吗?

@adobe/spacecat-shared-data-access 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@adobe/spacecat-shared-data-access 的下载量有多少?

根据 npm 官方下载统计,@adobe/spacecat-shared-data-access 在 2026/10/05 – 2026/11/03 这 30 天内被下载 6,056 次,日均约 202 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adobe/spacecat-shared-data-access 还在维护吗?

最新版本 4.41.0 发布于 2026/10/08(30 天前);至今共发布 607 个版本,其中过去 12 个月发布了 222 个;首次发布于 2023/12/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。