@lark-apaas/coding-miaoda-sandbox-skills 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -64,7 +64,7 @@ gate-tools:
|
|
|
64
64
|
| 规则 | 要求 |
|
|
65
65
|
|---|---|
|
|
66
66
|
| SQL 执行通道 | 只用 `+db-execute`,不要裸连数据库或调用其它 SQL 工具 |
|
|
67
|
-
| 查结构只认 DB | 表结构 / 字段 / 索引 / 约束一律 `+db-table-list` / `+db-table-get --table <table>`。`server/database/schema.ts` 只在写 TS
|
|
67
|
+
| 查结构只认 DB | 表结构 / 字段 / 索引 / 约束一律 `+db-table-list` / `+db-table-get --table <table>`。`server/database/schema.ts` 只在写 TS 代码对齐类型时读,**不能用来回答结构问题**——它是生成产物,除了可能滞后于 DB,还会静默丢掉条件索引的 `WHERE` 和 CHECK 约束(丢掉 WHERE 后,部分唯一索引在 `schema.ts` 里读起来是全表唯一) |
|
|
68
68
|
| `schema.ts` 禁止手改 | `schema.ts` 是 `npm run gen:db-schema` 的生成产物。DB 结构不对就改 DDL 后重跑 `npm run gen:db-schema`;DB 正确但 `schema.ts` 渲染错误时同样不手改,向用户说明是 schema 生成器 bug |
|
|
69
69
|
| DDL 后刷新 `schema.ts` | `+db-execute` **不会**自动刷新 `schema.ts`。每次 DDL 成功后立即在应用工程根目录执行 `npm run gen:db-schema`,再写引用新表 / 新列的 TS 代码 |
|
|
70
70
|
| 变更历史只认 DB | 结构变更历史用 `+db-changelog-list`。`git log -- server/database/schema.ts` 只是代码侧痕迹,**不等价**,不能用来回答"做过哪些 DDL 改动" |
|
|
@@ -133,6 +133,7 @@ CREATE POLICY "修改本人数据" ON <table>
|
|
|
133
133
|
5. 执行前向用户展示目标对象和变更内容;DROP / DROP COLUMN 还要说明数据丢失风险并取得明确授权。
|
|
134
134
|
6. 执行:`lark-cli apps +db-execute --app-id "$app_id" --sql "<ddl>" --yes`。多条 DDL 是一个逻辑单元时放在一次调用;需要全回滚则显式包事务。
|
|
135
135
|
7. DDL 成功后**立即**在应用工程根目录执行 `npm run gen:db-schema` 刷新 `server/database/schema.ts`——`+db-execute` 不会自动刷新。多批 DDL 每批成功后都跑一次。
|
|
136
|
+
8. 已存在的表只能用 `ALTER TABLE` 修改。`CREATE TABLE IF NOT EXISTS` 只会跳过建表,**不会**更新已有列的类型、默认值或约束;执行 `ALTER TABLE` 后再次用 `+db-table-get --table <table>` 回读 DDL,确认目标定义已经改变。
|
|
136
137
|
|
|
137
138
|
### DDL Do / Don't
|
|
138
139
|
|
|
@@ -160,6 +161,11 @@ CREATE POLICY "修改本人数据" ON <table>
|
|
|
160
161
|
| 标量子查询多行 | `VALUES((SELECT ...))` / `SET col=(SELECT ...)` 必须唯一或 `ORDER BY ... LIMIT 1` |
|
|
161
162
|
| 系统表查询 | 常规结构查询用 `+db-table-list/get`;系统表仅限 reference 中列出的白名单 |
|
|
162
163
|
| 多语句事务误判 | `A; B; C` 不自动包事务,B 失败时 A 不回滚;需要原子性用 `BEGIN/COMMIT` |
|
|
164
|
+
| 时间表达式依赖会话时区 | `timestamptz` 参与日期/小时计算、CHECK 或表达式索引时,显式写 `AT TIME ZONE 'UTC'` 或明确的业务时区;用跨日期边界的时间值验证结果,禁止依赖会话默认时区 |
|
|
165
|
+
|
|
166
|
+
### 写入前核对全部唯一性
|
|
167
|
+
|
|
168
|
+
新增或修改 `INSERT`、`UPDATE`、upsert 写入逻辑前,先用 `+db-table-get --table <table> --format pretty` 读取目标表完整 DDL,列出其中**全部** UNIQUE 约束和唯一索引(含条件索引、表达式索引),逐条检查本次写入是否可能冲突。`ON CONFLICT` 只处理它明确匹配的那一条唯一规则,不能挡住其它唯一索引;发现其它冲突时,应在写入前按业务语义处理,或将 `23505` 转成明确的冲突错误,禁止让它冒成 500。
|
|
163
169
|
|
|
164
170
|
## Data Types and Design
|
|
165
171
|
|
|
@@ -121,6 +121,7 @@ CREATE POLICY "修改本人数据" ON <table>
|
|
|
121
121
|
4. CREATE TABLE / CREATE INDEX / ALTER TABLE ADD COLUMN 必须带 `IF NOT EXISTS`,避免重复执行失败。
|
|
122
122
|
5. 执行前向用户展示目标对象和变更内容;DROP / DROP COLUMN 还要说明数据丢失风险并取得明确授权。
|
|
123
123
|
6. 执行:`miaoda db sql "<ddl>"`。多条 DDL 是一个逻辑单元时放在一次调用;需要全回滚则显式包事务。
|
|
124
|
+
7. 已存在的表只能用 `ALTER TABLE` 修改。`CREATE TABLE IF NOT EXISTS` 只会跳过建表,**不会**更新已有列的类型、默认值或约束;执行 `ALTER TABLE` 后再次用 `miaoda db schema get <table>` 回读 DDL,确认目标定义已经改变。
|
|
124
125
|
|
|
125
126
|
### DDL Do / Don't
|
|
126
127
|
|
|
@@ -149,6 +150,11 @@ CREATE POLICY "修改本人数据" ON <table>
|
|
|
149
150
|
| 标量子查询多行 | `VALUES((SELECT ...))` / `SET col=(SELECT ...)` 必须唯一或 `ORDER BY ... LIMIT 1` |
|
|
150
151
|
| 系统表查询 | 常规结构查询用 schema 命令;系统表仅限 reference 中列出的白名单 |
|
|
151
152
|
| 多语句事务误判 | `A; B; C` 不自动包事务,B 失败时 A 不回滚;需要原子性用 `BEGIN/COMMIT` |
|
|
153
|
+
| 时间表达式依赖会话时区 | `timestamptz` 参与日期/小时计算、CHECK 或表达式索引时,显式写 `AT TIME ZONE 'UTC'` 或明确的业务时区;用跨日期边界的时间值验证结果,禁止依赖会话默认时区 |
|
|
154
|
+
|
|
155
|
+
### 写入前核对全部唯一性
|
|
156
|
+
|
|
157
|
+
新增或修改 `INSERT`、`UPDATE`、upsert 写入逻辑前,先用 `miaoda db schema get <table>` 读取目标表完整 DDL,列出其中**全部** UNIQUE 约束和唯一索引(含条件索引、表达式索引),逐条检查本次写入是否可能冲突。`ON CONFLICT` 只处理它明确匹配的那一条唯一规则,不能挡住其它唯一索引;发现其它冲突时,应在写入前按业务语义处理,或将 `23505` 转成明确的冲突错误,禁止让它冒成 500。
|
|
152
158
|
|
|
153
159
|
## Data Types and Design
|
|
154
160
|
|
|
@@ -59,11 +59,11 @@ const userMap = new Map(users.map(u => [u.id, u]));
|
|
|
59
59
|
- N+1 queries: per-item DB calls inside loops — use batch or JOIN
|
|
60
60
|
- List endpoints without pagination — will break as data grows
|
|
61
61
|
- OFFSET pagination on large datasets — use cursor pagination (WHERE id > cursor)
|
|
62
|
-
- Multiple independent DB queries running serially — use Promise.all
|
|
62
|
+
- Multiple independent DB queries running serially — use Promise.all. The platform connection pool caps real concurrency at **7**; beyond that queries queue. It only pays off for IO-bound queries — CPU-heavy ones (large aggregations, big `generate_series`) still take the same total time, so for those the only fix is fewer/cheaper queries
|
|
63
63
|
- External API calls without timeout
|
|
64
|
-
- WHERE/ORDER BY columns missing indexes — **cross-check
|
|
64
|
+
- WHERE/ORDER BY columns missing indexes — **cross-check against the live DDL: for every ORDER BY or WHERE column in service queries, verify a matching index exists via `npx -y @lark-apaas/miaoda-data-cli db schema <table>`. Do NOT use `schema.ts` for this — it is a generated mirror that silently drops partial-index `WHERE` clauses and CHECK constraints, so an existing partial index reads as missing (and a partial unique index reads as full-table unique)**
|
|
65
65
|
- Aggregation done in JS instead of SQL layer — fetch all rows then compute in app is a common anti-pattern
|
|
66
|
-
- **Sorting fields used in list/ranking endpoints must have database indexes —
|
|
66
|
+
- **Sorting fields used in list/ranking endpoints must have database indexes — verify with `db schema <table>` (the live DDL), not `schema.ts`**
|
|
67
67
|
- **Date-range loop expansion in JS (CRITICAL)**: code shaped like `for (let d = new Date(start); d <= end; d.setDate(d.getDate()+1)) for (item of items) ...` is O(N×days). For 1000 records over 90 days = 90,000 ops per request. Must be pushed to SQL using `generate_series(start, end, '1 day') + unnest(array_field) + GROUP BY ... HAVING COUNT(*) > N` — let the database do the date×row cross product
|
|
68
68
|
- **Multiple endpoints sharing the same base data should be merged**: e.g. Dashboard frontend calling 4 separate stats endpoints, each internally re-querying the full teacher table to build a lookup map. Merge into a single overview endpoint that runs the 4 stats in parallel internally and shares one base-data query.
|
|
69
69
|
- **Stats / Dashboard endpoints must have a default time window**: any query that scans the entire fact table for aggregation (`select ... from coursePlan` with no `where`) will degrade as data grows. Add a default `where startDate >= now() - interval '90 day'` (or business-appropriate window) so it can hit `idx_*_start_date`. Without a time window, even with indexes the planner may still seq-scan. If "all history" is required, use a materialized view or pre-aggregated table refreshed on schedule, never a live full-table scan.
|
|
@@ -59,11 +59,11 @@ const userMap = new Map(users.map(u => [u.id, u]));
|
|
|
59
59
|
- N+1 queries: per-item DB calls inside loops — use batch or JOIN
|
|
60
60
|
- List endpoints without pagination — will break as data grows
|
|
61
61
|
- OFFSET pagination on large datasets — use cursor pagination (WHERE id > cursor)
|
|
62
|
-
- Multiple independent DB queries running serially — use Promise.all
|
|
62
|
+
- Multiple independent DB queries running serially — use Promise.all. The platform connection pool caps real concurrency at **7**; beyond that queries queue. It only pays off for IO-bound queries — CPU-heavy ones (large aggregations, big `generate_series`) still take the same total time, so for those the only fix is fewer/cheaper queries
|
|
63
63
|
- External API calls without timeout
|
|
64
|
-
- WHERE/ORDER BY columns missing indexes — **cross-check
|
|
64
|
+
- WHERE/ORDER BY columns missing indexes — **cross-check against the live DDL: for every ORDER BY or WHERE column in service queries, verify a matching index exists via `npx -y @lark-apaas/miaoda-data-cli db schema <table>`. Do NOT use `schema.ts` for this — it is a generated mirror that silently drops partial-index `WHERE` clauses and CHECK constraints, so an existing partial index reads as missing (and a partial unique index reads as full-table unique)**
|
|
65
65
|
- Aggregation done in JS instead of SQL layer — fetch all rows then compute in app is a common anti-pattern
|
|
66
|
-
- **Sorting fields used in list/ranking endpoints must have database indexes —
|
|
66
|
+
- **Sorting fields used in list/ranking endpoints must have database indexes — verify with `db schema <table>` (the live DDL), not `schema.ts`**
|
|
67
67
|
- **Date-range loop expansion in JS (CRITICAL)**: code shaped like `for (let d = new Date(start); d <= end; d.setDate(d.getDate()+1)) for (item of items) ...` is O(N×days). For 1000 records over 90 days = 90,000 ops per request. Must be pushed to SQL using `generate_series(start, end, '1 day') + unnest(array_field) + GROUP BY ... HAVING COUNT(*) > N` — let the database do the date×row cross product
|
|
68
68
|
- **Multiple endpoints sharing the same base data should be merged**: e.g. Dashboard frontend calling 4 separate stats endpoints, each internally re-querying the full teacher table to build a lookup map. Merge into a single overview endpoint that runs the 4 stats in parallel internally and shares one base-data query.
|
|
69
69
|
- **Stats / Dashboard endpoints must have a default time window**: any query that scans the entire fact table for aggregation (`select ... from coursePlan` with no `where`) will degrade as data grows. Add a default `where startDate >= now() - interval '90 day'` (or business-appropriate window) so it can hit `idx_*_start_date`. Without a time window, even with indexes the planner may still seq-scan. If "all history" is required, use a materialized view or pre-aggregated table refreshed on schedule, never a live full-table scan.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-miaoda-sandbox-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "Miaoda 合并沙箱 skills 包(包含原 miaoda-skills 的 miaoda / miaoda-modern / miaoda-design / shared 四条业务线);发布公网 npm,随沙箱运行时经 update-skills 同步到 .agent/skills/",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|