Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Add 'money' to FieldType enum - Create MoneyConfigSchema with precision, currencyMode, and defaultCurrency - Create MoneyValueSchema for runtime value structure - Extend FieldSchema with optional moneyConfig property - Add Field.money() helper factory function - Add comprehensive tests (71 total field tests, all passing) - Generate JSON schemas and documentation - All 1638 tests passing Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Document flexible currency code validation approach - Support ISO 4217, cryptocurrency, and custom codes - Explain validation can be stricter at application layer - All tests passing, no security vulnerabilities Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
错了,我想改的就是标准的currency类型,不是增加新的类型 @copilot |
- Remove 'money' field type from enum (use existing 'currency' instead) - Rename MoneyConfig to CurrencyConfig - Rename MoneyValue to CurrencyValue - Change moneyConfig to currencyConfig in FieldSchema - Update all tests to use currency type instead of money type - Remove Field.money() helper (use Field.currency() instead) - Delete MoneyConfig/MoneyValue schemas and docs - Generate new CurrencyConfig/CurrencyValue schemas and docs - All 1638 tests passing Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
已修正。现在多币种支持已应用到标准的 主要更改:
使用示例: Field.currency({
name: 'price',
currencyConfig: {
precision: 2,
currencyMode: 'dynamic',
defaultCurrency: 'USD'
}
})提交: 0a470eb |
There was a problem hiding this comment.
Pull request overview
This PR enhances the existing currency field type with multi-currency support using a physical column splitting strategy. It introduces two new schemas (CurrencyConfigSchema and CurrencyValueSchema) that enable currency fields to support either dynamic (user-selectable) or fixed (single) currency modes, with different storage strategies for each.
Changes:
- Added
CurrencyConfigSchemawith configurable precision (0-10), currency mode (dynamic/fixed), and default currency code - Added
CurrencyValueSchemafor runtime representation of currency values with amount and currency code - Extended
FieldSchemawith optionalcurrencyConfigproperty for currency field configuration - Comprehensive test suite covering all new schemas and edge cases including cryptocurrency support
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/data/field.zod.ts | Added CurrencyConfigSchema and CurrencyValueSchema with proper validation, extended FieldSchema with currencyConfig property, added type exports |
| packages/spec/src/data/field.test.ts | Comprehensive test coverage for CurrencyConfigSchema, CurrencyValueSchema, and currency field integration including edge cases |
| packages/spec/package.json | Version bump from 0.3.1 to 0.3.2 |
| packages/spec/package-lock.json | Updated lockfile version references |
| packages/spec/json-schema/data/CurrencyConfig.json | Generated JSON schema for CurrencyConfig matching Zod schema |
| packages/spec/json-schema/data/CurrencyValue.json | Generated JSON schema for CurrencyValue matching Zod schema |
| packages/spec/json-schema/data/Field.json | Updated with currencyConfig property definition |
| packages/spec/json-schema/data/Object.json | Updated with currencyConfig property definition |
| packages/spec/json-schema/ui/FieldWidgetProps.json | Updated with currencyConfig property definition |
| content/docs/references/data/field/Field.mdx | Added currencyConfig property to field reference documentation |
| content/docs/references/data/field/CurrencyConfig.mdx | New reference documentation for CurrencyConfig schema |
| content/docs/references/data/field/CurrencyValue.mdx | New reference documentation for CurrencyValue schema |
Files not reviewed (1)
- packages/spec/package-lock.json: Language not supported
| * Configuration for currency field type supporting multi-currency | ||
| * | ||
| * Note: Currency codes are validated by length only (3 characters) to support: | ||
| * - Standard ISO 4217 codes (USD, EUR, CNY, etc.) | ||
| * - Cryptocurrency codes (BTC, ETH, etc.) | ||
| * - Custom business-specific codes | ||
| * Stricter validation can be implemented at the application layer based on business requirements. | ||
| */ | ||
| export const CurrencyConfigSchema = z.object({ | ||
| precision: z.number().int().min(0).max(10).default(2).describe('Decimal precision (default: 2)'), | ||
| currencyMode: z.enum(['dynamic', 'fixed']).default('dynamic').describe('Currency mode: dynamic (user selectable) or fixed (single currency)'), | ||
| defaultCurrency: z.string().length(3).default('CNY').describe('Default or fixed currency code (ISO 4217, e.g., USD, CNY, EUR)'), |
There was a problem hiding this comment.
The existing documentation in content/docs/guides/field-types.mdx shows currency fields using precision and scale as direct field properties (lines 192-193), but the new implementation introduces currencyConfig.precision. This creates potential confusion about which approach users should follow.
While both approaches are technically valid since FieldSchema supports both general precision/scale properties and the new currencyConfig, the documentation should be updated to clarify:
- Whether
currencyConfig.precisionshould be preferred for currency fields - The relationship between field-level
precision/scaleandcurrencyConfig.precision - Example usage showing the new multi-currency features (dynamic vs fixed modes, currency selection)
Consider updating the field-types guide to demonstrate the new multi-currency capabilities introduced in this PR.
| * Configuration for currency field type supporting multi-currency | |
| * | |
| * Note: Currency codes are validated by length only (3 characters) to support: | |
| * - Standard ISO 4217 codes (USD, EUR, CNY, etc.) | |
| * - Cryptocurrency codes (BTC, ETH, etc.) | |
| * - Custom business-specific codes | |
| * Stricter validation can be implemented at the application layer based on business requirements. | |
| */ | |
| export const CurrencyConfigSchema = z.object({ | |
| precision: z.number().int().min(0).max(10).default(2).describe('Decimal precision (default: 2)'), | |
| currencyMode: z.enum(['dynamic', 'fixed']).default('dynamic').describe('Currency mode: dynamic (user selectable) or fixed (single currency)'), | |
| defaultCurrency: z.string().length(3).default('CNY').describe('Default or fixed currency code (ISO 4217, e.g., USD, CNY, EUR)'), | |
| * Configuration for the `currency` field type, including multi-currency behavior. | |
| * | |
| * Precision & scale: | |
| * - For currency fields, `currencyConfig.precision` SHOULD be treated as the | |
| * authoritative decimal precision. | |
| * - Generic field-level `precision` / `scale` (on the parent FieldSchema) remain | |
| * available for backward compatibility and for non-currency numeric fields, but | |
| * tooling and documentation SHOULD prefer `currencyConfig.precision` when the | |
| * field `type === 'currency'`. | |
| * | |
| * Multi-currency behavior: | |
| * - `currencyMode: 'dynamic'` → each record/value can specify its own currency | |
| * (see CurrencyValueSchema.currency), with `defaultCurrency` used as the | |
| * initial/default selection. | |
| * - `currencyMode: 'fixed'` → all values for the field use a single, fixed | |
| * currency equal to `defaultCurrency`. | |
| * | |
| * Currency codes: | |
| * - Validated only by length (3 characters) to support: | |
| * - Standard ISO 4217 codes (USD, EUR, CNY, etc.) | |
| * - Cryptocurrency codes (BTC, ETH, etc.) | |
| * - Custom business-specific codes | |
| * - Stricter validation can be implemented at the application layer based on | |
| * business requirements. | |
| */ | |
| export const CurrencyConfigSchema = z.object({ | |
| precision: z | |
| .number() | |
| .int() | |
| .min(0) | |
| .max(10) | |
| .default(2) | |
| .describe('Currency-specific decimal precision (default: 2)'), | |
| currencyMode: z | |
| .enum(['dynamic', 'fixed']) | |
| .default('dynamic') | |
| .describe('Currency mode: dynamic (per-value currency) or fixed (single currency for the field)'), | |
| defaultCurrency: z | |
| .string() | |
| .length(3) | |
| .default('CNY') | |
| .describe('Default or fixed 3-letter currency code (e.g., USD, CNY, EUR)'), |
Enhances the existing
currencyfield type with multi-currency support using physical column splitting: one logical field maps to 1-2 database columns depending on currency mode.Schema Additions
CurrencyConfigSchema
precision: 0-10 decimal places (default: 2)currencyMode:'dynamic'(user-selectable) or'fixed'(single currency)defaultCurrency: 3-character code (default: 'CNY')CurrencyValueSchema
{ value: number, currency: string }FieldSchema Extension
currencyConfigpropertyColumn Splitting Strategy
Dynamic mode (default): 2 physical columns
{fieldName}→ DECIMAL(18, precision){fieldName}_cur→ VARCHAR(3)Fixed mode: 1 physical column
{fieldName}→ DECIMAL(18, precision)Usage
Design Notes
Currency validation uses length-only (3 chars) to support ISO 4217, cryptocurrencies (BTC, ETH), and custom business codes. Stricter validation deferred to application layer per business requirements.
Enables direct SQL queries on currency:
SELECT * FROM projects WHERE price_cur = 'USD' AND price > 10000with indexing support on both columns.Original prompt
这是一个基于**“物理列自动分裂(Automated Physical Column Splitting)”**策略的完整技术设计方案。
该方案的核心理念是:UI 层的“单字段”体验,DB 层的“多字段”存储,中间通过引擎自动转换。
系统设计文档:低代码平台多币种字段 (Money Type)
数据流向分为三个阶段:
我们需要在 grid.zod.ts 中定义货币的元数据。这决定了引擎如何处理这个字段。
2.1 扩展 Zod Schema
import { z } from 'zod';
// 货币专用配置
const MoneyConfigSchema = z.object({
// 精度:默认 2 位
precision: z.number().int().default(2),
// 模式:
// 'dynamic': 允许用户选币种 (数据库生成 _cur 列)
// 'fixed': 固定币种 (数据库不生成 _cur 列,节省空间)
currencyMode: z.enum(['dynamic', 'fixed']).default('dynamic'),
// 如果是 fixed 模式,必须指定币种
defaultCurrency: z.string().default('CNY'),
});
export const GridColumnSchema = z.object({
// 字段名 (逻辑名),例如 "contract_amount"
dataIndex: z.string(),
title: z.string(),
// 类型标识
valueType: z.enum(['text', 'number', 'money', 'date']), // ...其他类型
// 挂载配置
moneyConfig: MoneyConfigSchema.optional(),
});
这是最关键的“脏活”部分。当用户点击“发布表单”时,后端引擎需要解析 Schema 并生成 SQL。
3.1 命名规范 (Naming Convention)
3.2 DDL 生成器伪代码 (DDL Generator)
function generateCreateTableSQL(tableName: string, columns: GridColumnSchema[]) {
let fieldDefinitions = [];
columns.forEach(col => {
// 处理 Money 类型
if (col.valueType === 'money') {
const { precision = 2, currencyMode = 'dynamic' } = col.moneyConfig || {};
});
return
CREATE TABLE ${tableName} ( id INT PRIMARY KEY, ${fieldDefinitions.join(', ')} );;}
引擎充当 ORM (Object-Relational Mapper) 的角色,负责“欺骗”前端。
4.1 写入转换 (Write / Unboxing)
前端发来的是一个对象,后端要拆散存入数据库。
// Input: 前端提交的 JSON
const payload = {
title: "服务器采购",
price: { value: 5000.00, currency: "USD" } // 复合对象
};
async function insertRecord(tableSchema, payload) {
const dbRow = {};
tableSchema.columns.forEach(col => {
const key = col.dataIndex;
const value = payload[key];
});
// 生成 SQL: INSERT INTO ... (title, price, price_cur) VALUES ...
return await db.insert(dbRow);
}
4.2 读取转换 (Read / Boxing)
数据库查出来是散的,后端要组装好发给前端。
// Input: 数据库查出的 Raw Data
const dbRows = [
{ id: 1, title: "服务器采购", price: 5000.00, price_cur: "USD" }
];
function transformRows(tableSchema, dbRows) {
return dbRows.map(row => {
const apiRow = { ...row };
});
}
解决“用户如何筛选”的问题。
用户在前端配置筛选器:{ field: "price", operator: ">", value: 100 }。
用户的直觉是“金额大于100”,他通常不管币种,或者默认指当前币种。
function buildWhereClause(filter) {
const { field, operator, value } = filter;
// 假设 schema 中 price 是 money 类型
// 场景 1: 用户只查数值 (WHERE price > 100)
// 这是最自然的 SQL 映射,直接查主列
if (typeof value === 'number') {
return
\${field}` ${operator} ${value}`;}
// 场景 2: 用户精确查特定币种的金额 (WHERE price = 100 AND price_cur = 'USD')
// 前端传来的 value 可能是 { value: 100, currency: 'USD' }
if (typeof value === 'object') {
return
(\${field}` ${operator} ${value.value} AND `${field}_cur` = '${value.currency}')`;}
}
前端拿到的数据已经是 { value: 100, currency: 'USD' },组件渲染就非常简单了。
6.1 MoneyInput 组件
import { Select, InputNumber } from 'antd';
const MoneyInput = ({ value = {}, onChange }) => {
// value 格式: { value: 100, currency: 'CNY' }
const handleCurrencyChange = (newCur) => {
onChange({ ...value, currency: newCur });
};
const handleValueChange = (newVal) => {
onChange({ ...value, value: newVal });
};
return (
<div style={{ display: 'flex' }}>
<Select
value={value.curr...
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.