[refactor] migrate NGO models to BI Table API based on MobX-Lark 2.8#63
[refactor] migrate NGO models to BI Table API based on MobX-Lark 2.8#63
Conversation
[optimize] update Upstream packages
📝 WalkthroughWalkthrough将数据源从 Strapi API 迁移至飞书多维表格。核心改动:Organization 模型层从 MobX-Strapi 替换为 Lark/koajax,统计维度从 Changes
Sequence Diagram(s)sequenceDiagram
participant Page as Pages/NGO/[year]
participant Model as OrganizationModel
participant Lark as Lark API
participant Store as systemStore
participant Map as CityStatisticMap
Page->>Model: getStatistic({startYear})
Model->>Lark: 查询数据表记录
Lark-->>Model: 返回 city 维度数据
Model->>Model: extractFields() 规范化 city/entityType
Model-->>Page: OrganizationStatistic['city']
Page->>Map: 传递 statistic.city
Map->>Map: Object.entries(city) 遍历
Note over Map: 城市名称规范化<br/>(去掉市|自治州|特别行政区)
Map->>Store: 查询 cityCoordinate[normalizedCity]
Store-->>Map: 返回坐标数据
Map->>Map: 生成地图标记并渲染
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 关键审查点:
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR refactors the NGO data layer to use Feishu/Lark Bitable (via MobX-Lark 2.8) instead of the previous Strapi/package-based model, and updates upstream dependencies accordingly.
Changes:
- Replaced the NGO
OrganizationModelimplementation withBiDataTable/BiSearchbased models and added Bitable-backed statistic table models. - Updated NGO pages/components to use the new field names/statistic shapes (
startYear,city, etc.). - Updated dependencies (Next.js, MobX-Lark, tooling) and adjusted project setup docs/config (
pnpm, registry cleanup, new env vars).
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates local dev commands to pnpm. |
| pnpm-lock.yaml | Locks upgraded framework/tooling/library versions. |
| package.json | Bumps Next.js/MobX-Lark/etc.; removes GitHub Packages dependency. |
| .npmrc | Removes GitHub Packages registry configuration (no longer needed). |
| .env | Points NGO data source to Lark proxy + adds NGO Bitable IDs. |
| pages/api/Lark/bitable/v1/[...slug].ts | Simplifies Bitable proxy API route to a single safe Koa handler. |
| models/Organization.ts | Migrates NGO model + statistics models to MobX-Lark BI Table API. |
| models/System.ts | Switches NGO search model mapping to SearchOrganizationModel. |
| pages/NGO/[year]/index.tsx | Updates stats query/filter and map input to new city statistic. |
| pages/NGO/[year]/landscape.tsx | Updates grouping filter to startYear. |
| components/Organization/Charts.tsx | Updates chart inputs from coverageArea to city. |
| components/Organization/Card.tsx | Adapts card fields to new model shape (website/wechatPublic/services). |
| components/Organization/Landscape.tsx | Adapts type import + string-safety for name slicing. |
| components/Map/CityStatisticMap.tsx | Adapts prop type and normalizes city names for coordinate lookup. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| ...fields, | ||
| city: normalizeTextArray(city as TableCellText[]), | ||
| entityType: (entityType as TableCellRelation[])?.map(normalizeText), | ||
| tags: (tags as TableCellRelation[])?.map(normalizeText).toString().split(','), |
There was a problem hiding this comment.
tags normalization can throw when tags is missing/empty: optional chaining only applies to .map(), but .toString() is then called on the (possibly) undefined result. Consider defaulting to an empty array (or chaining ?. through) before calling toString()/split, so records without tags don’t crash SSR/client rendering.
| tags: (tags as TableCellRelation[])?.map(normalizeText).toString().split(','), | |
| tags: ((tags as TableCellRelation[])?.map(normalizeText) ?? []).toString().split(','), |
| const years = await yearStore.getAll(); | ||
|
|
||
| Object.assign(organizationStore, { sort: { establishedDate: 'desc' } }); | ||
|
|
||
| const [{ establishedDate: end } = {}] = await organizationStore.getList({}, 1, 1); | ||
|
|
||
| const startYear = new Date(start || now).getFullYear(), | ||
| endYear = new Date(end || now).getFullYear(); | ||
|
|
||
| return [startYear, endYear] as const; | ||
| const [{ name: start } = {}] = years.sort( | ||
| ({ name: a }, { name: b }) => (a as number) - (b as number), | ||
| ); | ||
| const [{ name: end } = {}] = years.sort( | ||
| ({ name: a }, { name: b }) => (b as number) - (a as number), | ||
| ); | ||
| return [start, end] as [number, number]; |
There was a problem hiding this comment.
getYearRange() assumes name is a number ((a as number) - (b as number) and returns [start, end] as [number, number]), but TableCellValue is often a string. This can yield NaN sorting and return non-numeric years. Consider coercing via Number(name) (or parseInt) and computing min/max without mutating/sorting the whole array.
| 'year' | 'city' | 'entityType' | 'serviceCategory', | ||
| Record<string, number> | ||
| >; |
There was a problem hiding this comment.
OrganizationStatistic is defined to always include a year distribution, but getStatistic() only populates city, entityType, and serviceCategory and then type-casts. This makes the type misleading and encourages unsafe casts; either add a real year aggregation or remove year from the required keys in the type.
| 'year' | 'city' | 'entityType' | 'serviceCategory', | |
| Record<string, number> | |
| >; | |
| 'city' | 'entityType' | 'serviceCategory', | |
| Record<string, number> | |
| > & { | |
| year?: Record<string, number>; | |
| }; |
| const group = list.map(({ name, [key]: count }) => count && [name, count]).filter(Boolean) as [ | ||
| string, | ||
| number, | ||
| ][]; | ||
|
|
There was a problem hiding this comment.
countAll() drops valid zero counts because it filters with count && .... Also name/count are TableCellValue, so returning them as [string, number] without coercion can leak strings into the result. Consider filtering on count != null (or typeof count === 'number') and converting name/count to the expected primitive types.
| const group = list.map(({ name, [key]: count }) => count && [name, count]).filter(Boolean) as [ | |
| string, | |
| number, | |
| ][]; | |
| const group = list.flatMap(({ name, [key]: count }) => { | |
| const normalizedCount = | |
| typeof count === 'number' | |
| ? count | |
| : typeof count === 'string' && count.trim() | |
| ? Number(count) | |
| : NaN; | |
| if (Number.isNaN(normalizedCount)) return []; | |
| return [[String(name), normalizedCount] as [string, number]]; | |
| }); |
| ## 开始 | ||
|
|
||
| ```bash | ||
| npm install | ||
| npm run dev | ||
| # or | ||
| yarn install | ||
| yarn dev | ||
| pnpm install | ||
| pnpm dev | ||
| ``` |
There was a problem hiding this comment.
The updated deps/tooling (e.g., Next.js 16.2.2 / ESLint 10.2.x) have stricter Node engine requirements than just “Node 20+” in some cases. It would help to document the minimum supported Node.js version here (and that pnpm is required), so pnpm install doesn’t fail unexpectedly for contributors on older 20.x minors.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/Organization/Card.tsx (1)
38-50:⚠️ Potential issue | 🟡 Minor用户可见文本应使用 i18n
t()函数
title="WWW"和title="WeChat"是硬编码字符串,违反了国际化要求。所有用户可见的文本必须使用t()函数。🌐 建议修改
{website && ( - <Button title="WWW" size="sm" target="_blank" href={website?.toString()}> + <Button title={t('www')} size="sm" target="_blank" href={website?.toString()}> <Icon name="globe2" /> </Button> )} {wechatPublic && ( <Button - title="WeChat" + title={t('wechat')} size="sm" variant="success" onClick={() => (this.showQRC = !this.showQRC)} >需要在
render()方法中从 context 获取t函数,并在翻译文件中添加相应的键值。As per coding guidelines: "All user-facing text MUST use the i18n t() function (no hardcoded strings)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Organization/Card.tsx` around lines 38 - 50, Replace the hardcoded button titles with i18n keys by using the t() function from context inside the component's render() method: obtain t (e.g., const { t } = this.context or from props) and set title={t('organization.www')} and title={t('organization.wechat')} for the Button instances referenced in Card.tsx (the Button with Icon "globe2" and the Button with Icon "chat-fill" that toggles this.showQRC); also add corresponding keys ("organization.www" and "organization.wechat") to the translation files.
🧹 Nitpick comments (3)
components/Organization/Card.tsx (2)
28-28: 在非响应式方法中应使用this.props而非this.observedProps根据 MobX observer 组件的最佳实践,
renderIcon()是从render()调用的普通方法,不是响应式上下文(如 computed getter 或 observable 方法)。在此处使用this.observedProps可能导致不必要的重新渲染或响应式追踪问题。♻️ 建议修改
renderIcon() { - const { website, wechatPublic } = this.observedProps; + const { website, wechatPublic } = this.props;Based on learnings: "In MobX observer components that extend ObservedComponent, only access this.observedProps inside reactive contexts (e.g., computed getters, observable methods). In non-reactive methods such as render(), prefer using this.props."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Organization/Card.tsx` at line 28, renderIcon is a plain (non-reactive) method but reads props via this.observedProps; change it to use this.props instead to avoid unintended MobX reactive tracking and extra re-renders—locate the renderIcon method in the Organization Card component and replace any uses of this.observedProps (e.g., this.observedProps.website, this.observedProps.wechatPublic) with this.props.website and this.props.wechatPublic (or destructure const { website, wechatPublic } = this.props) so only reactive contexts use observedProps.
81-85:services as string[]类型断言存在潜在风险根据
Organization类型定义,services的类型是TableCellValue(联合类型),而非显式的string[]。虽然此写法与代码库中其他地方的模式一致(如getStatistic方法),但如果 API 返回非数组类型,.map()调用会抛出 TypeError。建议增加类型守卫或使用更安全的写法:
🛡️ 可选的防御性写法
{services && ( <BadgeBar className="justify-content-end" - list={(services as string[]).map(text => ({ text }))} + list={Array.isArray(services) ? services.map(text => ({ text: String(text) })) : []} /> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Organization/Card.tsx` around lines 81 - 85, The code casts services to string[] and calls .map on it which can throw if services (from the Organization TableCellValue union) is not an array; in the Card component add a defensive type guard before mapping (e.g. use Array.isArray(services) and ensure items are strings or coerce via String(item)), produce an empty list fallback when not an array, and pass that safe array to BadgeBar (referencing the services prop and BadgeBar usage in Card.tsx); this mirrors safer handling used elsewhere (e.g., getStatistic) and prevents runtime TypeError.models/Organization.ts (1)
17-19: 补上./Base的模型层初始化。这个 model 已经成了新的数据入口,但当前没有先
import './Base'。这样基础配置会依赖别的文件是否碰巧先加载,后面很容易和其他 model 的 client 行为分叉。♻️ 建议修正
+import './Base'; import { STRAPI_API_HOST } from './configuration'; import { SearchableFilter } from './System'; import { observable } from 'mobx';As per coding guidelines:
models/**/*.ts: Import './Base' in model files to ensure proper GitHub client configuration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@models/Organization.ts` around lines 17 - 19, The Organization model is missing the mandatory model-layer initialization import, so ensure you add the side-effect import for Base at the top of models/Organization.ts by importing './Base' before any other model-specific imports (so the Base initialization runs and configures the GitHub/client behavior); place the import('./Base') above or before uses of STRAPI_API_HOST, SearchableFilter, or observable to guarantee consistent client setup across models.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/Organization/Card.tsx`:
- Around line 77-78: Replace the unsafe string coercion using "+ ''" for name
and entityType with a null-safe fallback like the nullish coalescing used
elsewhere (e.g., use name ?? '' and entityType ?? '') so that the display and
the BadgeBar list item do not render literal "undefined" or "null"; update the
occurrences around the JSX where name and the BadgeBar list use entityType to
use the null-safe fallback.
In `@models/Organization.ts`:
- Around line 43-46: The exported OrganizationStatistic type currently requires
'year' but getStatistic() only returns city, entityType and serviceCategory,
causing a mismatched type and a misleading assertion; update the
OrganizationStatistic declaration to match the actual return shape (e.g.,
Record<'city' | 'entityType' | 'serviceCategory', Record<string, number>>) or
alternatively implement the missing year aggregation inside getStatistic()
(refer to the OrganizationStatistic type and the getStatistic() function to
apply the chosen fix).
- Around line 68-80: The keyMap object is missing mappings for email, codeLink,
and tags which are referenced elsewhere; update the keyMap (the keyMap variable
in models/Organization.ts) to include Chinese labels for "email", "codeLink",
and "tags" so Feishu header mapping and validation succeed; ensure the labels
align with existing style (e.g., short Chinese descriptors) and verify
extractFields() and searchKeys still find their corresponding keys after the
additions.
---
Outside diff comments:
In `@components/Organization/Card.tsx`:
- Around line 38-50: Replace the hardcoded button titles with i18n keys by using
the t() function from context inside the component's render() method: obtain t
(e.g., const { t } = this.context or from props) and set
title={t('organization.www')} and title={t('organization.wechat')} for the
Button instances referenced in Card.tsx (the Button with Icon "globe2" and the
Button with Icon "chat-fill" that toggles this.showQRC); also add corresponding
keys ("organization.www" and "organization.wechat") to the translation files.
---
Nitpick comments:
In `@components/Organization/Card.tsx`:
- Line 28: renderIcon is a plain (non-reactive) method but reads props via
this.observedProps; change it to use this.props instead to avoid unintended MobX
reactive tracking and extra re-renders—locate the renderIcon method in the
Organization Card component and replace any uses of this.observedProps (e.g.,
this.observedProps.website, this.observedProps.wechatPublic) with
this.props.website and this.props.wechatPublic (or destructure const { website,
wechatPublic } = this.props) so only reactive contexts use observedProps.
- Around line 81-85: The code casts services to string[] and calls .map on it
which can throw if services (from the Organization TableCellValue union) is not
an array; in the Card component add a defensive type guard before mapping (e.g.
use Array.isArray(services) and ensure items are strings or coerce via
String(item)), produce an empty list fallback when not an array, and pass that
safe array to BadgeBar (referencing the services prop and BadgeBar usage in
Card.tsx); this mirrors safer handling used elsewhere (e.g., getStatistic) and
prevents runtime TypeError.
In `@models/Organization.ts`:
- Around line 17-19: The Organization model is missing the mandatory model-layer
initialization import, so ensure you add the side-effect import for Base at the
top of models/Organization.ts by importing './Base' before any other
model-specific imports (so the Base initialization runs and configures the
GitHub/client behavior); place the import('./Base') above or before uses of
STRAPI_API_HOST, SearchableFilter, or observable to guarantee consistent client
setup across models.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8174f2ee-3d2c-4aa0-b284-da35d213acf8
⛔ Files ignored due to path filters (5)
.envis excluded by none and included by none.npmrcis excluded by none and included by noneREADME.mdis excluded by!**/*.mdand included by nonepackage.jsonis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamland included by none
📒 Files selected for processing (9)
components/Map/CityStatisticMap.tsxcomponents/Organization/Card.tsxcomponents/Organization/Charts.tsxcomponents/Organization/Landscape.tsxmodels/Organization.tsmodels/System.tspages/NGO/[year]/index.tsxpages/NGO/[year]/landscape.tsxpages/api/Lark/bitable/v1/[...slug].ts
| {name + ''} | ||
| <BadgeBar className="ms-2" list={[{ text: entityType + '' }]} /> |
There was a problem hiding this comment.
字符串强制转换可能产生 "undefined" 或 "null" 文本
使用 + '' 进行字符串转换时,如果 name 或 entityType 为 undefined 或 null,会渲染出字面量字符串 "undefined" 或 "null",这对用户体验不友好。
建议使用与第 91 行一致的安全写法:
🛡️ 建议修改
<Card.Title>
- {name + ''}
- <BadgeBar className="ms-2" list={[{ text: entityType + '' }]} />
+ {name?.toString() ?? ''}
+ <BadgeBar className="ms-2" list={[{ text: entityType?.toString() ?? '' }]} />
</Card.Title>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {name + ''} | |
| <BadgeBar className="ms-2" list={[{ text: entityType + '' }]} /> | |
| <Card.Title> | |
| {name?.toString() ?? ''} | |
| <BadgeBar className="ms-2" list={[{ text: entityType?.toString() ?? '' }]} /> | |
| </Card.Title> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/Organization/Card.tsx` around lines 77 - 78, Replace the unsafe
string coercion using "+ ''" for name and entityType with a null-safe fallback
like the nullish coalescing used elsewhere (e.g., use name ?? '' and entityType
?? '') so that the display and the BadgeBar list item do not render literal
"undefined" or "null"; update the occurrences around the JSX where name and the
BadgeBar list use entityType to use the null-safe fallback.
| export type OrganizationStatistic = Record< | ||
| 'coverageArea' | 'locale' | 'entityType' | 'serviceCategory', | ||
| 'year' | 'city' | 'entityType' | 'serviceCategory', | ||
| Record<string, number> | ||
| >; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and read the Organization.ts file
find . -name "Organization.ts" -type fRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 115
🏁 Script executed:
# Let's search for the OrganizationStatistic type and getStatistic method
rg -n "OrganizationStatistic|getStatistic" --type tsRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1730
🏁 Script executed:
# Read the Organization.ts file around the relevant lines
cat -n models/Organization.ts | head -150 | tail -110Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 4234
🏁 Script executed:
# Check how getStatistic is used and if .year is accessed
rg -B2 -A2 "getStatistic\(\)" --type ts --type tsxRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 118
🏁 Script executed:
# Check if year field is accessed on the returned statistic
rg "\.year" --type ts --type tsx | grep -i statisticRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 118
🏁 Script executed:
# Check OrganizationYearStatisticModel for context
cat -n models/Organization.ts | sed -n '169,185p'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 667
🏁 Script executed:
# Search for getStatistic usages correctly
rg "getStatistic" -A 3 -B 1Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 679
🏁 Script executed:
# Search for access to .year on statistic variable
rg "statistic\.year" -B 2 -A 2Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 73
🏁 Script executed:
# Check the Charts.tsx component to see how OrganizationStatistic is used
cat -n components/Organization/Charts.tsx | head -20Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1038
OrganizationStatistic 的导出类型与实际返回值不匹配。
getStatistic() 方法(第 119-132 行)仅构造了 city、entityType 和 serviceCategory 三个字段,但第 43-46 行的类型声明要求 'year' | 'city' | 'entityType' | 'serviceCategory' 四个字段都存在。第 132 行使用 as OrganizationStatistic 的类型断言掩盖了这个不一致。
调用方(如 components/Organization/Charts.tsx)只解构访问 { entityType, serviceCategory, city},从未使用 year 字段,而运行时其值为 undefined。建议将类型收窄为实际返回的形状:
Record<'city' | 'entityType' | 'serviceCategory', Record<string, number>>
或在 getStatistic() 中补上 year 聚合逻辑(如果业务需要)。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@models/Organization.ts` around lines 43 - 46, The exported
OrganizationStatistic type currently requires 'year' but getStatistic() only
returns city, entityType and serviceCategory, causing a mismatched type and a
misleading assertion; update the OrganizationStatistic declaration to match the
actual return shape (e.g., Record<'city' | 'entityType' | 'serviceCategory',
Record<string, number>>) or alternatively implement the missing year aggregation
inside getStatistic() (refer to the OrganizationStatistic type and the
getStatistic() function to apply the chosen fix).
| keyMap = { | ||
| name: '常用名称', | ||
| code: '机构信用代码', | ||
| entityType: '实体类型', | ||
| startDate: '成立时间', | ||
| startYear: '成立年份', | ||
| city: '城市', | ||
| website: '机构官网', | ||
| wechatPublic: '机构微信公众号', | ||
| description: '机构/项目简介', | ||
| coverageArea: '机构/项目辐射范围', | ||
| services: '关于行业类服务对象', | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate the Organization.ts file
find . -name "Organization.ts" -type fRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 115
🏁 Script executed:
# Let's examine the Organization.ts file to verify the review comment
wc -l ./models/Organization.tsRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 119
🏁 Script executed:
# Read the file sections mentioned in the review
cat -n ./models/Organization.ts | sed -n '60,220p'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 5528
🏁 Script executed:
# Let's search for how keyMap is used in the codebase
rg "keyMap" --type ts --type tsx -B 2 -A 2Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 118
🏁 Script executed:
# Let's search for how keyMap is used, using ts filetype only
rg "keyMap" -t ts -B 2 -A 2Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 807
🏁 Script executed:
# Search for how keyMap is being accessed or used in the codebase
rg "\.keyMap|keyMap\[" -t ts -B 3 -A 3Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 73
🏁 Script executed:
# Let's check the parent class BiDataTable to understand keyMap usage
fd "BiDataTable" -t fRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 73
🏁 Script executed:
# Let's check what BiDataTable is and where it comes from
rg "BiDataTable" -t ts -B 2 -A 2 | head -50Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1764
🏁 Script executed:
# Let's find where BiDataTable is imported from
rg "from.*BiDataTable|import.*BiDataTable" -t ts -B 1 -A 1 | head -30Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 210
🏁 Script executed:
# Let's check the Organization.ts imports to confirm BiDataTable source
head -20 ./models/Organization.tsRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 552
🏁 Script executed:
# Let's search for any usage of extractFields or searchKeys to understand the data flow
rg "extractFields" -t ts -B 3 -A 3Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 4233
🏁 Script executed:
# Let's check if there are any other similar model files to see the pattern of keyMap usage
cat -n ./models/Project.ts | sed -n '50,130p'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 455
🏁 Script executed:
# Let's see the Project.ts keyMap
cat -n ./models/Project.ts | sed -n '1,50p'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1556
🏁 Script executed:
# Search for keyMap in Project.ts
grep -n "keyMap" ./models/Project.tsRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 73
🏁 Script executed:
# Let's check Activity.ts to see if it has a keyMap
cat -n ./models/Activity.ts | sed -n '1,100p'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 3301
🏁 Script executed:
# Let's search for how the missing fields (email, tags, codeLink) are defined in the Organization type
cat -n ./models/Organization.ts | head -60 | tail -20Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 822
补齐 keyMap 缺失的字段映射
keyMap 中缺少 email、codeLink、tags 三个字段的中文标签映射。这些字段已被代码活跃消费,但缺少对应的飞书表头映射可能导致字段同步失效或表结构验证问题:
codeLink:在extractFields()(90-100 行) 和searchKeys(209 行) 中使用tags:在extractFields()(98 行) 中使用email:在searchKeys(207 行) 中使用
需要为这三个字段添加对应的中文标签到 keyMap 中。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@models/Organization.ts` around lines 68 - 80, The keyMap object is missing
mappings for email, codeLink, and tags which are referenced elsewhere; update
the keyMap (the keyMap variable in models/Organization.ts) to include Chinese
labels for "email", "codeLink", and "tags" so Feishu header mapping and
validation succeed; ensure the labels align with existing style (e.g., short
Chinese descriptors) and verify extractFields() and searchKeys still find their
corresponding keys after the additions.
Summary by CodeRabbit
发布说明
新功能
改进