@wdyy/skills 0.1.19 → 0.1.21
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.
- package/.well-known/skills/index.json +5 -5
- package/.well-known/skills/wdyy-deployment-standard/SKILL.md +1 -0
- package/.well-known/skills/wdyy-ui/SKILL.md +50 -0
- package/.well-known/skills/wdyy-ui/agents/openai.yaml +7 -0
- package/.well-known/skills/wdyy-ui/assets/template/clinical-workstation.html +66 -0
- package/.well-known/skills/wdyy-ui/assets/template/common-business.html +64 -0
- package/.well-known/skills/wdyy-ui/assets/template/logo.png +0 -0
- package/.well-known/skills/wdyy-ui/assets/template/pharmacy-management.html +61 -0
- package/.well-known/skills/wdyy-ui/assets/template/styles.css +603 -0
- package/.well-known/skills/wdyy-ui/references/ui-standard.md +100 -0
- package/README.md +10 -5
- package/lib/wdyy-cli.js +35 -7
- package/package.json +4 -2
- package/.well-known/skills/wdyy-api-standard/SKILL.md +0 -52
- package/.well-known/skills/wdyy-api-standard/agents/openai.yaml +0 -4
- package/.well-known/skills/wdyy-api-standard/reference/api-demo.md +0 -27
- package/.well-known/skills/wdyy-api-standard/reference/api-his-sql.md +0 -53
- package/.well-known/skills/wdyy-api-standard/reference/api-sms.md +0 -67
- package/.well-known/skills/wdyy-api-standard/reference/api-sso.md +0 -87
- package/.well-known/skills/wdyy-api-standard/scripts/ensure-sso-env-example.mjs +0 -76
- package/.well-known/skills/wdyy-api-standard/scripts/ensure-sso-env-example.test.mjs +0 -66
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
```text
|
|
8
8
|
AGENTS.md 全局基线、技术栈、OpenSpec 流程与 Skill 路由
|
|
9
9
|
OpenSpec 单项变更的需求、设计、任务、实施与归档
|
|
10
|
-
.well-known/skills/wdyy-*
|
|
10
|
+
.well-known/skills/wdyy-* 数据库、日志、部署、医院 UI 的专业规则
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
当前 `.well-known/skills/index.json` 登记 5 个企业 Skill。
|
|
@@ -15,10 +15,10 @@ OpenSpec 单项变更的需求、设计、任务、实
|
|
|
15
15
|
| Skill | 职责 |
|
|
16
16
|
|---|---|
|
|
17
17
|
| `wdyy-database-standard` | PostgreSQL 18 数据库设计、版本化迁移、数据兼容性与 DDL 校验。 |
|
|
18
|
-
| `wdyy-api-standard` | 根据“获取XX数据”“使用XX接口”等需求定位接口 reference,并按已确认契约写入调用方式。 |
|
|
19
18
|
| `wdyy-bug-record` | 手动汇总当前任务已解决的问题,并按模板追加至 `docs/bug_record.md`。 |
|
|
20
19
|
| `wdyy-logging-standard` | 结构化日志、敏感字段移除、traceId、前端异常上报与日志轮转。 |
|
|
21
20
|
| `wdyy-deployment-standard` | 在 Linux、macOS Docker Desktop 或 Windows Docker Desktop + WSL2/Linux 容器模式中,使用本地基础镜像构建前后端独立镜像,分别导出并统一压缩交付,远程数据库直连并一键部署整个项目栈。 |
|
|
21
|
+
| `wdyy-ui` | 统一医院信息系统页面的布局、清新生命绿配色、医院 Logo、常用组件和响应式行为,不定义业务流程、接口或数据模型。 |
|
|
22
22
|
|
|
23
23
|
## 使用方式
|
|
24
24
|
|
|
@@ -29,7 +29,11 @@ npm install -g @wdyy/skills
|
|
|
29
29
|
wdyy init
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`wdyy init` 会在系统用户目录 `.agents/skills/` 安装或更新 5 个受管理 Skill
|
|
32
|
+
`wdyy init` 会在系统用户目录 `.agents/skills/` 安装或更新 5 个受管理 Skill,并删除其中旧的 `wdyy-api-standard`,但不在目标项目创建 `.agents/skills/`。它还会执行 `git init`,创建或补齐 `.gitignore` 的 `.env`、`node_modules/`、`venv/`、`.pnpm-store/`、`logs/`、`dist/`、`.DS_Store`、`*.tar.gz` 规则,创建 `src/frontend`、`src/backend`、`database`、`deploy`、`docs`、`scripts`、`tests`,并以相同的“前端、后端、数据库、API、部署”结构创建缺失的 `.env` 与 `.env.example`。默认前端监听 `0.0.0.0:5173`、后端监听 `0.0.0.0:3000`、数据库端口为 `5432`;已有环境文件保持不变。随后命令执行 `openspec init`、创建或追加 `docs/bug_record.md` 模板。若目标项目已有 `AGENTS.md`,原文件保持不变,命令将生成 `AGENTS_new.md`。
|
|
33
|
+
|
|
34
|
+
生成的项目规则要求:接口实现仅以工程师人工提供的 YAML 文件为依据;未提供 YAML 或接口契约不完整时必须询问工程师,不得从旧 Skill、reference、远程目录或其他来源推断或编造。既有项目需要重新执行 `wdyy init` 才会生成包含该要求的新规则文件。
|
|
35
|
+
|
|
36
|
+
生成的项目规则还要求:每个经确认的变更完成并通过全部测试与验证后创建对应 Git commit,不得提交未完成或验证失败的状态;每个变更依据对应 OpenSpec 的需求、场景和任务编写或更新相关测试,交付前必须解决全部测试与验证失败,不得跳过或隐瞒。既有项目重新执行 `wdyy init` 后,可从新生成的 `AGENTS_new.md` 审阅并合并该规则。
|
|
33
37
|
|
|
34
38
|
### 仅更新已安装的 Skill
|
|
35
39
|
|
|
@@ -38,14 +42,15 @@ wdyy init
|
|
|
38
42
|
```bash
|
|
39
43
|
npm install -g @wdyy/skills@latest
|
|
40
44
|
wdyy update-skill wdyy-logging-standard
|
|
45
|
+
wdyy update-skill wdyy-ui
|
|
41
46
|
wdyy update-skill --all
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
`wdyy update-skill <skill-name>` 只接受 5 个受管理的 `wdyy-*` Skill,且仅替换系统用户目录 `.agents/skills/` 中的指定目录;`--all` 替换全部 5 个受管理 Skill
|
|
49
|
+
`wdyy update-skill <skill-name>` 只接受 5 个受管理的 `wdyy-*` Skill,且仅替换系统用户目录 `.agents/skills/` 中的指定目录;`--all` 替换全部 5 个受管理 Skill并删除旧的 `wdyy-api-standard`。两种方式都不会写入调用项目的 `AGENTS.md`、`AGENTS_new.md`、`docs/bug_record.md` 或 `.agents/skills/`,也不会执行 `openspec init`。首次初始化或需要重新生成项目规则时,使用 `wdyy init`。
|
|
45
50
|
|
|
46
51
|
部署 Skill 生成的标准流程为:在 Linux、macOS Docker Desktop,或 Windows Docker Desktop + WSL2/Linux 文件系统的项目根目录执行 `./deploy.sh build`,只使用本地已有的 Node、Nginx 基础镜像构建 `<项目名>_frontend:latest` 与 `<项目名>_backend:latest`,分别导出两个镜像 tar,并统一封装为 `deploy/<项目名>-docker.tar.gz`。工程师人工复制并解压该压缩包,在受支持的 Linux engine 上无参数执行 `./deploy.sh` 即校验、加载并整体部署前后端;macOS/arm64 Docker Desktop 可通过禁止拉取的实际目标镜像探针验证并运行 `linux/amd64` 包,而不是仅按 Server 原生架构拒绝。另可执行 `./deploy.sh stop` 和 `./deploy.sh status`。数据库通过 `.env` 直连远程 PostgreSQL,不生成数据库容器;前后端的宿主机与容器端口都来自 `.env`,容器名与镜像 repository 相同,使用 `unless-stopped`,并验证 24 小时制 CST(UTC+8);日志直接保存在部署根 `logs/`。不支持原生 PowerShell 或 Windows 容器模式。
|
|
47
52
|
|
|
48
|
-
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive
|
|
53
|
+
中大型变更先使用 `/opsx:explore` 或 `/opsx:propose <change-name>`,在方案确认后使用 `/opsx:apply`;完成验证后使用 `/opsx:archive`。涉及数据库、日志、部署或医院信息系统 UI 时,按 `AGENTS.md` 的路由加载相应企业 Skill;接口实现仅依据工程师人工提供的 YAML。所有 URL、端口和 IP 地址从 `.env` 读取,不得硬编码;所有项目管理的日志直接写入项目根目录 `logs/`;生产部署不使用蓝绿发布。不得提交秘密或生产环境 `.env`。项目完成后创建 `README.md`,说明技术栈、目录结构和安装部署。
|
|
49
54
|
|
|
50
55
|
## 固定技术栈
|
|
51
56
|
|
package/lib/wdyy-cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { access, appendFile, cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { access, appendFile, cp, lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -7,12 +7,14 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
8
|
const managedSkillNames = [
|
|
9
9
|
'wdyy-database-standard',
|
|
10
|
-
'wdyy-api-standard',
|
|
11
10
|
'wdyy-bug-record',
|
|
12
11
|
'wdyy-logging-standard',
|
|
13
|
-
'wdyy-deployment-standard'
|
|
12
|
+
'wdyy-deployment-standard',
|
|
13
|
+
'wdyy-ui'
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
+
const removedManagedSkillNames = ['wdyy-api-standard'];
|
|
17
|
+
|
|
16
18
|
const defaultProjectDirectories = [
|
|
17
19
|
'src/frontend',
|
|
18
20
|
'src/backend',
|
|
@@ -81,16 +83,21 @@ const generatedAgentsContent = `# 企业开发规则
|
|
|
81
83
|
| 场景 | 必须使用的 Skill |
|
|
82
84
|
|---|---|
|
|
83
85
|
| PostgreSQL 表、DDL、迁移、数据兼容性 | \`wdyy-database-standard\` |
|
|
84
|
-
| 获取已登记接口数据、使用已登记接口 | \`wdyy-api-standard\` |
|
|
85
86
|
| 汇总当前任务已解决的 BUG、漏洞或运行异常 | 手动调用 \`wdyy-bug-record\`,追加写入 \`docs/bug_record.md\` |
|
|
86
87
|
| 结构化日志、traceId、完整原始入参、前端异常上报 | \`wdyy-logging-standard\` |
|
|
87
88
|
| 统一 Docker 交付、Linux/Mac/Windows WSL2 双镜像、远程数据库与整栈一键部署 | \`wdyy-deployment-standard\` |
|
|
89
|
+
| 医院信息系统页面的新建、改造或审查 | \`wdyy-ui\`;不负责定义业务流程、接口或数据模型 |
|
|
88
90
|
|
|
89
91
|
## 通用边界
|
|
90
92
|
|
|
91
93
|
- 项目根目录应提供真实可执行的 \`pnpm dev\`、\`pnpm build\`、\`pnpm test\`、\`pnpm lint\` 命令;\`pnpm dev\` 只负责前后端的启动,不得启动或管理数据库。
|
|
94
|
+
- 接口实现仅以工程师人工提供的 YAML 文件为依据。未提供 YAML 或 YAML 中的接口契约不完整时,必须询问工程师;不得从已删除的 Skill、旧 reference、远程目录或其他来源推断或编造接口细节。
|
|
95
|
+
- 生成的接口文档必须符合 OpenAPI 3.1 规范,并声明 \`openapi: 3.1.0\`。
|
|
96
|
+
- 在下载或安装任何项目依赖前,必须先从官方发布渠道确认当前最新稳定版本,并检查该版本及其传递依赖的公开漏洞修复状态。原则上必须使用最新稳定版本;不得使用已过时的依赖,也不得使用存在未修复高危(High/Critical)漏洞的依赖。若最新稳定版本不兼容,或不存在满足安全要求的版本,必须停止安装并报告依赖名称、候选版本、兼容性及漏洞证据,等待工程师确认;不得擅自降级、忽略审计结果或添加豁免。
|
|
92
97
|
- 代码、配置、迁移、测试与部署文件必须以实际实现为准;不得用模板虚构已实现能力。
|
|
93
98
|
- 所有需求(如数据库变更、流程变更、功能变更、从零开发项目等),必须先使用\`openspec-explore\`技能确认需求,再根据实际情况,使用\`openspec\`的其它技能。
|
|
99
|
+
- 每个经确认的变更完成并通过全部测试与验证后,必须创建一个对应的 Git commit,以支持追踪和回滚;不得提交未完成或验证失败的状态。
|
|
100
|
+
- 每个变更都必须依据对应 OpenSpec 的需求、场景和任务编写或更新相关测试;交付前必须解决所有测试与验证失败,不得跳过或隐瞒失败。
|
|
94
101
|
- 所有项目管理的日志必须直接写入项目根目录 \`logs/\`,不得通过环境变量改写目录或创建服务、实例、日期等日志子目录。
|
|
95
102
|
- 生产部署必须使用 \`wdyy-deployment-standard\`:在 Linux、macOS Docker Desktop 或 Windows Docker Desktop + WSL2/Linux 容器模式中,只使用已有基础镜像构建前后端两个独立 Linux 镜像,分别导出后封装为一个压缩包;数据库直连远程实例且不得使用数据库容器;解压后无参数执行 \`./deploy.sh\`,一次性部署整个前后端项目栈。禁止原生 PowerShell、Windows 容器模式、自动拉取镜像、交互菜单和单独操作一个服务。
|
|
96
103
|
- 不得静默吞错、跳过验证、以兜底逻辑掩盖未决问题。
|
|
@@ -128,6 +135,22 @@ async function installManagedSkills(skillsRoot, writtenPaths, skillNames = manag
|
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
|
|
138
|
+
async function removeDeprecatedManagedSkills(skillsRoot, writtenPaths) {
|
|
139
|
+
for (const skillName of removedManagedSkillNames) {
|
|
140
|
+
const destination = join(skillsRoot, skillName);
|
|
141
|
+
try {
|
|
142
|
+
await lstat(destination);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error.code === 'ENOENT') continue;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await rm(destination, { recursive: true, force: true });
|
|
149
|
+
writtenPaths.push(destination);
|
|
150
|
+
console.log(`已删除旧 Skill: ${destination}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
131
154
|
function getGlobalSkillsRoot() {
|
|
132
155
|
return join(homedir(), '.agents', 'skills');
|
|
133
156
|
}
|
|
@@ -249,7 +272,9 @@ export async function run(argumentsList) {
|
|
|
249
272
|
const gitExitCode = initializeGit(process.cwd(), writtenPaths);
|
|
250
273
|
if (gitExitCode !== 0) return gitExitCode;
|
|
251
274
|
await writeAgentsFile(process.cwd(), writtenPaths);
|
|
252
|
-
|
|
275
|
+
const globalSkillsRoot = getGlobalSkillsRoot();
|
|
276
|
+
await installManagedSkills(globalSkillsRoot, writtenPaths);
|
|
277
|
+
await removeDeprecatedManagedSkills(globalSkillsRoot, writtenPaths);
|
|
253
278
|
await appendGitIgnoreEntries(process.cwd(), writtenPaths);
|
|
254
279
|
await createDefaultProjectLayout(process.cwd(), writtenPaths);
|
|
255
280
|
await appendBugRecordTemplate(process.cwd(), writtenPaths);
|
|
@@ -266,7 +291,8 @@ export async function run(argumentsList) {
|
|
|
266
291
|
|
|
267
292
|
if (argumentsList[0] === 'update-skill') {
|
|
268
293
|
const skillArguments = argumentsList.slice(1);
|
|
269
|
-
const
|
|
294
|
+
const updateAll = skillArguments.length === 1 && skillArguments[0] === '--all';
|
|
295
|
+
const skillNames = updateAll
|
|
270
296
|
? managedSkillNames
|
|
271
297
|
: skillArguments.length === 1 && managedSkillNames.includes(skillArguments[0])
|
|
272
298
|
? [skillArguments[0]]
|
|
@@ -279,7 +305,9 @@ export async function run(argumentsList) {
|
|
|
279
305
|
|
|
280
306
|
const writtenPaths = [];
|
|
281
307
|
try {
|
|
282
|
-
|
|
308
|
+
const globalSkillsRoot = getGlobalSkillsRoot();
|
|
309
|
+
await installManagedSkills(globalSkillsRoot, writtenPaths, skillNames);
|
|
310
|
+
if (updateAll) await removeDeprecatedManagedSkills(globalSkillsRoot, writtenPaths);
|
|
283
311
|
return 0;
|
|
284
312
|
} catch (error) {
|
|
285
313
|
console.error(`Skill 更新失败: ${error.message}`);
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdyy/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "企业内部开发 Skill 与项目初始化命令",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@12.1.0",
|
|
6
7
|
"bin": {
|
|
7
8
|
"wdyy": "bin/wdyy.js"
|
|
8
9
|
},
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
15
16
|
"engines": {
|
|
16
|
-
"node": ">=24"
|
|
17
|
+
"node": ">=24",
|
|
18
|
+
"pnpm": "12.1.0"
|
|
17
19
|
},
|
|
18
20
|
"scripts": {
|
|
19
21
|
"test": "node --test tests/*.test.mjs",
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: wdyy-api-standard
|
|
3
|
-
description: 根据“获取XX数据”“使用XX接口”“使用指定查询名称”等具体需求,定位已登记的接口 reference;对于 HIS SQL 查询,从远程查询目录按 name 精确匹配,并按已确认的接口契约将调用方式写入程序。Use when 需要查询或实现一个已登记接口或 HIS SQL 查询的数据调用时。
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# 接口路由
|
|
8
|
-
|
|
9
|
-
## 用途
|
|
10
|
-
|
|
11
|
-
本 Skill 只负责根据具体需求定位接口 reference,并将已确认的接口调用方式写入程序。接口明细和 HIS SQL 查询目录使用规则一律维护在 `reference/` 中,每个接口一个文档。
|
|
12
|
-
|
|
13
|
-
## 何时使用
|
|
14
|
-
|
|
15
|
-
- 用户描述“获取XX数据”“使用XX接口”或等价的数据查询、接口调用需求时。
|
|
16
|
-
- 用户描述“使用 `<查询名称>`”并需要从已登记 HIS SQL 查询目录实现调用时。
|
|
17
|
-
- 需要将已登记接口的 URL、入参和返回值处理方式写入程序时。
|
|
18
|
-
|
|
19
|
-
## 路由步骤
|
|
20
|
-
|
|
21
|
-
1. 根据需求中的数据名称、业务对象或接口名称,在 `reference/api-*.md` 中查找候选业务接口,排除仅用于说明格式的 `api-demo.md`。
|
|
22
|
-
2. 若需求为“使用 `<查询名称>`”且未直接匹配接口名称,则读取 `reference/api-his-sql.md`,按其中的远程目录和查询名称匹配规则处理。
|
|
23
|
-
3. 仅在唯一匹配时读取对应 reference。匹配 `api-sso` 时,在写入调用代码前运行 `node <Skill目录>/scripts/ensure-sso-env-example.mjs --target <目标项目根目录>`:目标 `.env.example` 缺失、`SSO_BASE_URL` 重复或已有不同值时,明确报告错误并等待工程师处理;工程师确认替换唯一的不同值后,才使用 `--replace-existing` 重试。不得创建或修改 `.env`。
|
|
24
|
-
4. 使用对应 reference 的接口 URL、JSON 入参和 JSON 返回值编写调用方式。
|
|
25
|
-
5. 按 reference 的“备注”实现特殊调用要求。
|
|
26
|
-
6. 若没有匹配接口、存在多个候选接口,或 URL、入参、返回值不完整,先要求补充或确认接口信息;不得编造调用细节。
|
|
27
|
-
|
|
28
|
-
## 接口文档约定
|
|
29
|
-
|
|
30
|
-
- `reference/api-demo.md` 仅说明接口文档格式,不得作为业务调用依据;其中可使用 `XXX` 等占位内容。
|
|
31
|
-
- 一个接口对应一个 `reference/api-接口名称.md` 文件。
|
|
32
|
-
- 文件名去除 `.md` 后必须与“接口名称”字段一致。
|
|
33
|
-
- 每个业务接口文档必须按以下顺序包含六个一级标题:
|
|
34
|
-
|
|
35
|
-
```md
|
|
36
|
-
# 接口名称:api-xxx
|
|
37
|
-
|
|
38
|
-
# 接口用途:中文描述
|
|
39
|
-
|
|
40
|
-
# 接口URL:
|
|
41
|
-
|
|
42
|
-
# 入参格式:JSON
|
|
43
|
-
|
|
44
|
-
# 返回值:JSON
|
|
45
|
-
|
|
46
|
-
# 备注:特殊使用方法说明
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## 禁止事项
|
|
50
|
-
|
|
51
|
-
- 不得依据接口名称或业务描述猜测 URL、请求字段、返回字段或特殊调用方式。
|
|
52
|
-
- 不得将多个接口明细重新堆放回本 `SKILL.md`。
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# 模板示例
|
|
2
|
-
|
|
3
|
-
# 接口名称:api-demo
|
|
4
|
-
|
|
5
|
-
# 接口用途:XXX
|
|
6
|
-
|
|
7
|
-
# 接口URL:XXX
|
|
8
|
-
|
|
9
|
-
# 入参格式:JSON
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"visitId": "6275033"
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
# 返回值:JSON
|
|
18
|
-
|
|
19
|
-
```json
|
|
20
|
-
{
|
|
21
|
-
"code": 200,
|
|
22
|
-
"data": [{}, {}],
|
|
23
|
-
"msg": "success"
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
# 备注:使用就诊号作为 `visitId` 参数调用;调用前应由业务上下文提供实际就诊号。
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# 接口名称:api-his-sql
|
|
2
|
-
|
|
3
|
-
# 接口用途:执行 SQL 查询并返回查询结果
|
|
4
|
-
|
|
5
|
-
# 接口URL:http://172.26.1.64:8999/api/database/query
|
|
6
|
-
|
|
7
|
-
`POST` 请求,`Content-Type` 为 `application/json`。
|
|
8
|
-
|
|
9
|
-
HIS SQL 查询目录:
|
|
10
|
-
|
|
11
|
-
- 人工查看:`http://172.26.1.64:9003/lihuanyu/cache_query/-/blob/main/cache-query.json?ref_type=heads`
|
|
12
|
-
- 程序读取:`http://172.26.1.64:9003/lihuanyu/cache_query/-/raw/main/cache-query.json`
|
|
13
|
-
|
|
14
|
-
# 入参格式:JSON
|
|
15
|
-
|
|
16
|
-
```json
|
|
17
|
-
{
|
|
18
|
-
"sql": "<cache-query.json 中匹配对象的 sql 原文>",
|
|
19
|
-
"params": [
|
|
20
|
-
"<按照匹配对象 params 顺序提供的实际值>"
|
|
21
|
-
]
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
按以下步骤生成项目程序中的调用方式:
|
|
26
|
-
|
|
27
|
-
1. 每次实现调用时读取“程序读取”地址,解析当前 `cache-query.json`;不得使用本地缓存或本 reference 中的示例 SQL 代替远程目录。
|
|
28
|
-
2. 遍历 JSON 顶层对象的全部值,仅选择对象 `name` 与用户指定查询名称完全相等的候选项;不得使用 JSON 顶层键代替 `name` 匹配。
|
|
29
|
-
3. 仅允许一个匹配对象。零个匹配或多个同名匹配时,明确报告问题并停止生成调用方式。
|
|
30
|
-
4. 要求匹配对象包含非空字符串 `sql` 和数组 `params`。将 `sql` 原样写入请求体,不得生成、改写或拼接 SQL。
|
|
31
|
-
5. 将目录中的 `params` 视为实际参数值的名称和顺序说明,不得把这些名称直接作为实际值发送。若用户或业务上下文未提供全部实际值,按数组顺序要求补充。
|
|
32
|
-
6. 获得全部实际值后,按照目录 `params` 的相同顺序写入请求体 `params` 数组。
|
|
33
|
-
|
|
34
|
-
例如,用户描述“使用 `get_diagnosis_info`”时,按对象 `name` 匹配该查询,并依次要求提供“开始日期”“结束日期”“诊断名称”;SQL 必须使用该匹配对象当前登记的原文。
|
|
35
|
-
|
|
36
|
-
# 返回值:JSON
|
|
37
|
-
|
|
38
|
-
```json
|
|
39
|
-
{
|
|
40
|
-
"data": [
|
|
41
|
-
{
|
|
42
|
-
"SSUSR_RowId": 20292,
|
|
43
|
-
"SSUSR_Initials": "20684",
|
|
44
|
-
"SSUSR_Name": "马俊"
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
"success": true,
|
|
48
|
-
"count": 1,
|
|
49
|
-
"message": "查询执行成功"
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
# 备注:本 Skill 只将调用方式写入项目程序,不直接向本接口发送查询请求。远程目录不可访问、指定 `name` 不存在、存在多个同名对象、`sql` 不是非空字符串或 `params` 不是数组时,必须明确报告具体问题;不得使用旧副本、示例 SQL、顶层键匹配或自行生成 SQL 继续实现。
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# 接口名称:api-sms
|
|
2
|
-
|
|
3
|
-
# 接口用途:向一个或多个手机号发送相同的短信内容
|
|
4
|
-
|
|
5
|
-
# 接口URL:http://172.26.1.80:8998/api/sms/send
|
|
6
|
-
|
|
7
|
-
`POST` 请求,`Content-Type` 为 `application/json; charset=utf-8`。当前地址为内网默认地址;生产或测试环境地址如有差异,以部署方提供的地址为准。
|
|
8
|
-
|
|
9
|
-
# 入参格式:JSON
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"mobiles": [
|
|
14
|
-
"13800000001",
|
|
15
|
-
"13800000002"
|
|
16
|
-
],
|
|
17
|
-
"content": "【系统通知】您的验证码是1234"
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
- `mobiles`:必填,`array[string]`,接收短信的手机号列表;单次最多 200 个手机号。
|
|
22
|
-
- `content`:必填,`string`,短信正文内容。
|
|
23
|
-
- 接入方不传入 token、`userCode` 或 `token` 等短信网关凭证。
|
|
24
|
-
|
|
25
|
-
# 返回值:JSON
|
|
26
|
-
|
|
27
|
-
成功提交至短信网关时返回 HTTP `200`:
|
|
28
|
-
|
|
29
|
-
```json
|
|
30
|
-
{
|
|
31
|
-
"msgId": "2601291081805244415"
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
`msgId` 为短信网关消息流水号,表示请求已提交至网关,不表示短信已最终送达。
|
|
36
|
-
|
|
37
|
-
失败或被拦截时返回 `error` 字段。手机号列表为空或短信内容为空时返回 HTTP `400`:
|
|
38
|
-
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"error": "Error: Mobile number array cannot be empty."
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"error": "Error: Content cannot be empty."
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
触发发送限流时返回 HTTP `429`:
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"error": "Too many requests: 手机号138****0001获取短信过于频繁,请稍后再试"
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
短信网关拒绝或返回异常时,接口仍可能返回 HTTP `200`:
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"error": "-41"
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
# 备注:使用 `POST` 请求调用;响应体存在 `msgId` 才表示提交成功,存在 `error` 表示发送失败或被拦截,不能只根据 HTTP 状态码判断成功。调用方应避免在日志中完整输出手机号、短信内容及验证码。调用前确认短信签名与模板已报备,并遵守单次最多 200 个手机号、单 IP 每分钟最多 120 次、单手机号每分钟最多 3 次的限流规则。
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# 接口名称:api-sso
|
|
2
|
-
|
|
3
|
-
# 接口用途:接入统一认证中心,完成外部系统登录、Token 状态校验和登录态用户修改密码
|
|
4
|
-
|
|
5
|
-
# 接口URL:
|
|
6
|
-
|
|
7
|
-
默认认证中心基础地址为 `http://172.26.1.80:9002`。使用本接口前,先运行 `node <Skill目录>/scripts/ensure-sso-env-example.mjs --target <目标项目根目录>`,把 `SSO_BASE_URL=http://172.26.1.80:9002` 写入目标项目已有的 `.env.example`。部署环境地址如有差异,以认证中心提供的实际地址为准;不得在代码中硬编码认证中心地址、端口或 JWT 签名密钥。接口路径如下:
|
|
8
|
-
|
|
9
|
-
| 用途 | 方法 | 路径 |
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| 外部系统登录 | `POST` | `/api/v1/auth/login` |
|
|
12
|
-
| Token 验签 | `POST` | `/api/v1/auth/verify` |
|
|
13
|
-
| 修改密码 | `POST` | `/api/v1/auth/change-password` |
|
|
14
|
-
|
|
15
|
-
三者均使用 `application/json; charset=utf-8`。登录无需 Token;验签和改密请求使用 `Authorization: Bearer <JWT_TOKEN>` 传入登录成功后取得的 Token。
|
|
16
|
-
|
|
17
|
-
# 入参格式:JSON
|
|
18
|
-
|
|
19
|
-
登录请求:
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"employee_id": "<EMPLOYEE_ID>",
|
|
24
|
-
"password": "<PASSWORD>",
|
|
25
|
-
"sys_id": "<SYSTEM_ID>"
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
- `employee_id` 与 `username` 二选一,均按用户工号处理。
|
|
30
|
-
- `password`、`sys_id` 必填;`sys_id` 为已启用接入系统的 ID,可为 number 或 string。
|
|
31
|
-
|
|
32
|
-
验签请求可使用空 JSON 请求体,并在请求头携带 Bearer Token。修改密码请求:
|
|
33
|
-
|
|
34
|
-
```json
|
|
35
|
-
{
|
|
36
|
-
"old_password": "<OLD_PASSWORD>",
|
|
37
|
-
"new_password": "<NEW_PASSWORD>"
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
- 改密仍须在请求头携带 Bearer Token。
|
|
42
|
-
- `new_password` 长度必须为 6 至 100 个字符。
|
|
43
|
-
|
|
44
|
-
# 返回值:JSON
|
|
45
|
-
|
|
46
|
-
外部系统登录成功时,响应结构为:
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"code": 200,
|
|
51
|
-
"message": "<DISPLAY_MESSAGE>",
|
|
52
|
-
"data": {
|
|
53
|
-
"allowed": true,
|
|
54
|
-
"user_id": "<USER_ID>",
|
|
55
|
-
"employee_id": "<EMPLOYEE_ID>",
|
|
56
|
-
"name": "<USER_NAME>",
|
|
57
|
-
"token": "<JWT_TOKEN>",
|
|
58
|
-
"permissions": [
|
|
59
|
-
{
|
|
60
|
-
"dept_id": "<DEPARTMENT_ID>",
|
|
61
|
-
"dept_name": "<DEPARTMENT_NAME>",
|
|
62
|
-
"roles": [
|
|
63
|
-
{
|
|
64
|
-
"role_id": "<ROLE_ID>",
|
|
65
|
-
"role_name": "<ROLE_NAME>",
|
|
66
|
-
"description": "<ROLE_DESCRIPTION>"
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
验签成功时 `data` 含 `valid`、`allowed`、`user_id`、`employee_id`、`name` 和 `sys_id`。改密成功时 `data` 含 `user_id`、`employee_id` 和 `name`。
|
|
76
|
-
|
|
77
|
-
所有接口使用 `{ code, message, data }` 结构。HTTP 状态码及 `code` 的含义:`200` 成功、`400` 参数错误、`401` 认证失败或 Token 无效/过期、`403` 无目标系统权限或权限已失效、`404` 接入系统不存在或已停用、`500` 服务端异常。
|
|
78
|
-
|
|
79
|
-
# 备注:
|
|
80
|
-
|
|
81
|
-
- 不得依赖 `message` 的精确文案;登录和验签根据 HTTP 状态码、`code`、`data.valid`、`data.allowed` 判断结果。
|
|
82
|
-
- 登录成功后仅在需要的安全存储中保存 Token;日志、异常上报和前端状态中不得明文记录密码、Token、JWT 签名密钥或连接信息。
|
|
83
|
-
- 仅当登录响应 `data.allowed` 为 `true` 才进入目标系统;根据 `permissions` 内按科室聚合的角色控制业务入口。
|
|
84
|
-
- 服务端需要确认登录态或权限是否仍有效时调用验签接口。验签返回 `valid: false` 或 `allowed: false` 时,清除本地登录态并要求重新登录或联系管理员。
|
|
85
|
-
- 用户改密成功后,清除旧 Token 并提示用户使用新密码重新登录。
|
|
86
|
-
- 不得用管理端 `/api/auth/*` 接口替代本 reference 中的外部系统 `/api/v1/auth/*` 接口。
|
|
87
|
-
- `.env.example` 缺失时明确报错,不得创建该文件或修改 `.env`。已有相同 `SSO_BASE_URL` 时不重复写入;已有不同值时保留原值并等待工程师确认。工程师确认替换唯一的不同值后,才使用 `--replace-existing`;重复定义时始终报错。
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { access, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { constants } from 'node:fs';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_SSO_BASE_URL = 'http://172.26.1.80:9002';
|
|
6
|
-
|
|
7
|
-
function usage() {
|
|
8
|
-
throw new Error('Usage: node ensure-sso-env-example.mjs --target <project-root> [--replace-existing]');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function parseArguments(argumentsList) {
|
|
12
|
-
let target;
|
|
13
|
-
let replaceExisting = false;
|
|
14
|
-
|
|
15
|
-
for (let index = 0; index < argumentsList.length; index += 1) {
|
|
16
|
-
const argument = argumentsList[index];
|
|
17
|
-
if (argument === '--target') {
|
|
18
|
-
if (target || index + 1 >= argumentsList.length) usage();
|
|
19
|
-
target = argumentsList[index + 1];
|
|
20
|
-
index += 1;
|
|
21
|
-
} else if (argument === '--replace-existing') {
|
|
22
|
-
if (replaceExisting) usage();
|
|
23
|
-
replaceExisting = true;
|
|
24
|
-
} else {
|
|
25
|
-
usage();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!target) usage();
|
|
30
|
-
return { target: resolve(target), replaceExisting };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function findSsoBaseUrlLines(content) {
|
|
34
|
-
return content.split(/(?<=\n)/).flatMap((line, index) => {
|
|
35
|
-
const match = line.match(/^SSO_BASE_URL=([^\r\n]*)(\r?\n)?$/);
|
|
36
|
-
return match ? [{ index, value: match[1], line, newline: match[2] ?? '' }] : [];
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function main() {
|
|
41
|
-
const { target, replaceExisting } = parseArguments(process.argv.slice(2));
|
|
42
|
-
const examplePath = resolve(target, '.env.example');
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
await access(examplePath, constants.F_OK);
|
|
46
|
-
} catch {
|
|
47
|
-
throw new Error(`Missing required configuration file: ${examplePath}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const content = await readFile(examplePath, 'utf8');
|
|
51
|
-
const entries = findSsoBaseUrlLines(content);
|
|
52
|
-
if (entries.length > 1) throw new Error('Duplicate SSO_BASE_URL definitions in .env.example');
|
|
53
|
-
|
|
54
|
-
if (entries.length === 0) {
|
|
55
|
-
const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
|
|
56
|
-
await writeFile(examplePath, `${content}${separator}SSO_BASE_URL=${DEFAULT_SSO_BASE_URL}\n`);
|
|
57
|
-
process.stdout.write(`Added SSO_BASE_URL to ${examplePath}\n`);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const [entry] = entries;
|
|
62
|
-
if (entry.value === DEFAULT_SSO_BASE_URL) {
|
|
63
|
-
process.stdout.write(`SSO_BASE_URL is already configured in ${examplePath}\n`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!replaceExisting) {
|
|
68
|
-
throw new Error('SSO_BASE_URL already has a different value; retain it and obtain engineer confirmation before using --replace-existing');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const replacement = `SSO_BASE_URL=${DEFAULT_SSO_BASE_URL}${entry.newline}`;
|
|
72
|
-
await writeFile(examplePath, content.replace(entry.line, replacement));
|
|
73
|
-
process.stdout.write(`Replaced SSO_BASE_URL in ${examplePath}\n`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
await main();
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { execFile } from 'node:child_process';
|
|
3
|
-
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import test from 'node:test';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { promisify } from 'node:util';
|
|
9
|
-
|
|
10
|
-
const run = promisify(execFile);
|
|
11
|
-
const script = join(dirname(fileURLToPath(import.meta.url)), 'ensure-sso-env-example.mjs');
|
|
12
|
-
const defaultValue = 'SSO_BASE_URL=http://172.26.1.80:9002';
|
|
13
|
-
const created = [];
|
|
14
|
-
|
|
15
|
-
async function fixture(content) {
|
|
16
|
-
const root = await mkdtemp(join(tmpdir(), 'wdyy-api-sso-'));
|
|
17
|
-
created.push(root);
|
|
18
|
-
if (content !== undefined) await writeFile(join(root, '.env.example'), content);
|
|
19
|
-
return root;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function invoke(root, ...argumentsList) {
|
|
23
|
-
return run(process.execPath, [script, '--target', root, ...argumentsList]);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
test.after(async () => {
|
|
27
|
-
await Promise.all(created.map((path) => rm(path, { recursive: true, force: true })));
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('缺少 .env.example 时明确失败且不创建文件', async () => {
|
|
31
|
-
const root = await fixture();
|
|
32
|
-
await assert.rejects(invoke(root), /Missing required configuration file/);
|
|
33
|
-
await assert.rejects(readFile(join(root, '.env.example')));
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('追加默认 SSO 地址且保持 .env.example 其他内容', async () => {
|
|
37
|
-
const root = await fixture('API_URL=/api\n');
|
|
38
|
-
await invoke(root);
|
|
39
|
-
assert.equal(await readFile(join(root, '.env.example'), 'utf8'), `API_URL=/api\n${defaultValue}\n`);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('已存在默认 SSO 地址时不重复写入', async () => {
|
|
43
|
-
const root = await fixture(`${defaultValue}\n`);
|
|
44
|
-
await invoke(root);
|
|
45
|
-
assert.equal(await readFile(join(root, '.env.example'), 'utf8'), `${defaultValue}\n`);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('不同 SSO 地址默认保留且明确失败', async () => {
|
|
49
|
-
const root = await fixture('SSO_BASE_URL=http://sso.internal:9002\n');
|
|
50
|
-
const before = await readFile(join(root, '.env.example'));
|
|
51
|
-
await assert.rejects(invoke(root), /different value/);
|
|
52
|
-
assert.deepEqual(await readFile(join(root, '.env.example')), before);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('重复 SSO 地址明确失败且不修改文件', async () => {
|
|
56
|
-
const root = await fixture(`${defaultValue}\nSSO_BASE_URL=http://sso.internal:9002\n`);
|
|
57
|
-
const before = await readFile(join(root, '.env.example'));
|
|
58
|
-
await assert.rejects(invoke(root), /Duplicate SSO_BASE_URL definitions/);
|
|
59
|
-
assert.deepEqual(await readFile(join(root, '.env.example')), before);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('明确替换后更新唯一的不同 SSO 地址', async () => {
|
|
63
|
-
const root = await fixture('SSO_BASE_URL=http://sso.internal:9002\n');
|
|
64
|
-
await invoke(root, '--replace-existing');
|
|
65
|
-
assert.equal(await readFile(join(root, '.env.example'), 'utf8'), `${defaultValue}\n`);
|
|
66
|
-
});
|