@pylonts/dsl 1.1.16 → 1.1.17
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/dist/convert.d.ts +4 -5
- package/dist/dto.d.ts +13 -0
- package/dist/dto.js +28 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mysql-driver.js +3 -3
- package/dist/service.js +3 -0
- package/dist/third-service.d.ts +58 -2
- package/dist/third-service.js +28 -0
- package/dist/token.d.ts +39 -0
- package/dist/token.js +96 -0
- package/dist/typebox-driver.d.ts +13 -0
- package/dist/typebox-driver.js +120 -5
- package/docs/curd.md +150 -146
- package/docs/gen-login.md +136 -0
- package/docs/third-service.md +201 -151
- package/docs/token-migration.md +60 -0
- package/docs/token.md +341 -327
- package/docs/wechat.md +235 -0
- package/package.json +2 -2
- package/src/convert.ts +9 -9
- package/src/dto.ts +364 -331
- package/src/index.ts +1 -0
- package/src/mysql-driver.ts +108 -108
- package/src/service.ts +5 -0
- package/src/third-service.ts +86 -2
- package/src/token.ts +139 -0
- package/src/typebox-driver.ts +128 -6
package/docs/third-service.md
CHANGED
|
@@ -1,152 +1,202 @@
|
|
|
1
|
-
# 定义第三方服务(ThirdServiceSchema)
|
|
2
|
-
|
|
3
|
-
第三方服务适配器契约(如微信支付 tenpay、短信、文件存储)。`defineThirdService` 声明适配器类契约:构造函数配置 + 方法列表。
|
|
4
|
-
|
|
5
|
-
> 完整对接流程(前置准备 → 契约化 → gen third → 填充 client → 沙箱验证)见 [methodology/third-party-integration.md](../../docs/methodology/third-party-integration.md)。
|
|
6
|
-
|
|
7
|
-
与两个相近概念区分:
|
|
8
|
-
|
|
9
|
-
- `ServiceSchema`(service_schema/)——后端业务服务;
|
|
10
|
-
- `ThirdApiSchema`(project.config.ts `thirdApis`)——项目拓扑:第三方系统的目录归属,如 `wx/`、`ble/`。
|
|
11
|
-
|
|
12
|
-
`ThirdServiceSchema.schema` 引用 `ThirdApiSchema` 实例(拓扑引用)。第三方方法实现在外部系统,仅声明契约、不建模内部流程。
|
|
13
|
-
|
|
14
|
-
## 定义
|
|
15
|
-
|
|
16
|
-
```ts
|
|
17
|
-
import { buildInput, buildOutput, CodeException, defineThirdService, dtoField, intField, IOException, stringField } from '@pylonts/dsl';
|
|
18
|
-
import { wx } from '../project.config';
|
|
19
|
-
|
|
20
|
-
const totalFee = intField({ optional: false, label: '金额(分)' });
|
|
21
|
-
|
|
22
|
-
export const wxPayService = defineThirdService({
|
|
23
|
-
schema: wx,
|
|
24
|
-
name: 'WxPayService',
|
|
25
|
-
description: '微信支付服务(tenpay APIv2)',
|
|
26
|
-
methods: {
|
|
27
|
-
getPayParams: {
|
|
28
|
-
args: buildInput('PayParams', {
|
|
29
|
-
out_trade_no: dtoField(stringField({ maxLength: 32, optional: false, label: '订单号' })),
|
|
30
|
-
total_fee: dtoField(totalFee),
|
|
31
|
-
// Same fact and same type as the entity column — shared instance.
|
|
32
|
-
openid: dtoField(user.columns.openid),
|
|
33
|
-
}),
|
|
34
|
-
results: buildOutput('PayParamsResult', {
|
|
35
|
-
appId: dtoField(stringField({ optional: false, label: 'appId' })),
|
|
36
|
-
paySign: dtoField(stringField({ optional: false, label: '签名' })),
|
|
37
|
-
}),
|
|
38
|
-
throws: [CodeException, IOException],
|
|
39
|
-
description: '获取支付参数',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
- `methods` 是 **map**:key 即方法名(构建器写回 `method.name`),value 为 `ThirdServiceMethodDef`。
|
|
46
|
-
- 每个方法的 `args` / `results` 各是一个 `DtoMessage`,用 `buildInput` / `buildOutput` 构建。`buildInput` 构建 `args`(输入消息),`buildOutput` 构建 `results`(输出消息)——与业务 DTO 同一 POJO 聚合,字段以 `dtoField(field)` 包装,map key 即线格式(wire-format)字段名,**原样保留协议拼写**(`out_trade_no`、`appId`,不做 camelCase)。
|
|
47
|
-
- `throws`(**必填**):每个方法必须声明 **`CodeException` + `IOException`** 两个异常——`CodeException`(第三方返回的业务错误码)与 `IOException`(网络/超时/不可恢复故障)。两个异常都来自 `@pylonts/core`,**不能自定义、不能替换**(`pylonts gen third` 会校验,缺失即报错拒绝生成)。原因:第三方集成有两类必然失败——业务层失败(第三方返回错误码,需转译给调用方)与传输层失败(网络/超时,需按故障重试或上报),client 骨架的异常翻译依赖这两个契约。
|
|
48
|
-
- `
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
1
|
+
# 定义第三方服务(ThirdServiceSchema)
|
|
2
|
+
|
|
3
|
+
第三方服务适配器契约(如微信支付 tenpay、短信、文件存储)。`defineThirdService` 声明适配器类契约:构造函数配置 + 方法列表。
|
|
4
|
+
|
|
5
|
+
> 完整对接流程(前置准备 → 契约化 → gen third → 填充 client → 沙箱验证)见 [methodology/third-party-integration.md](../../docs/methodology/third-party-integration.md)。
|
|
6
|
+
|
|
7
|
+
与两个相近概念区分:
|
|
8
|
+
|
|
9
|
+
- `ServiceSchema`(service_schema/)——后端业务服务;
|
|
10
|
+
- `ThirdApiSchema`(project.config.ts `thirdApis`)——项目拓扑:第三方系统的目录归属,如 `wx/`、`ble/`。
|
|
11
|
+
|
|
12
|
+
`ThirdServiceSchema.schema` 引用 `ThirdApiSchema` 实例(拓扑引用)。第三方方法实现在外部系统,仅声明契约、不建模内部流程。
|
|
13
|
+
|
|
14
|
+
## 定义
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { buildInput, buildOutput, CodeException, defineThirdService, dtoField, intField, IOException, stringField } from '@pylonts/dsl';
|
|
18
|
+
import { wx } from '../project.config';
|
|
19
|
+
|
|
20
|
+
const totalFee = intField({ optional: false, label: '金额(分)' });
|
|
21
|
+
|
|
22
|
+
export const wxPayService = defineThirdService({
|
|
23
|
+
schema: wx,
|
|
24
|
+
name: 'WxPayService',
|
|
25
|
+
description: '微信支付服务(tenpay APIv2)',
|
|
26
|
+
methods: {
|
|
27
|
+
getPayParams: {
|
|
28
|
+
args: buildInput('PayParams', {
|
|
29
|
+
out_trade_no: dtoField(stringField({ maxLength: 32, optional: false, label: '订单号' })),
|
|
30
|
+
total_fee: dtoField(totalFee),
|
|
31
|
+
// Same fact and same type as the entity column — shared instance.
|
|
32
|
+
openid: dtoField(user.columns.openid),
|
|
33
|
+
}),
|
|
34
|
+
results: buildOutput('PayParamsResult', {
|
|
35
|
+
appId: dtoField(stringField({ optional: false, label: 'appId' })),
|
|
36
|
+
paySign: dtoField(stringField({ optional: false, label: '签名' })),
|
|
37
|
+
}),
|
|
38
|
+
throws: [CodeException, IOException],
|
|
39
|
+
description: '获取支付参数',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- `methods` 是 **map**:key 即方法名(构建器写回 `method.name`),value 为 `ThirdServiceMethodDef`。
|
|
46
|
+
- 每个方法的 `args` / `results` 各是一个 `DtoMessage`,用 `buildInput` / `buildOutput` 构建。`buildInput` 构建 `args`(输入消息),`buildOutput` 构建 `results`(输出消息)——与业务 DTO 同一 POJO 聚合,字段以 `dtoField(field)` 包装,map key 即线格式(wire-format)字段名,**原样保留协议拼写**(`out_trade_no`、`appId`,不做 camelCase)。
|
|
47
|
+
- `throws`(**必填**):每个方法必须声明 **`CodeException` + `IOException`** 两个异常——`CodeException`(第三方返回的业务错误码)与 `IOException`(网络/超时/不可恢复故障)。两个异常都来自 `@pylonts/core`,**不能自定义、不能替换**(`pylonts gen third` 会校验,缺失即报错拒绝生成)。原因:第三方集成有两类必然失败——业务层失败(第三方返回错误码,需转译给调用方)与传输层失败(网络/超时,需按故障重试或上报),client 骨架的异常翻译依赖这两个契约。
|
|
48
|
+
- `format`(可选):网关**数据格式**——`'form'`(urlencoded)/ `'json'`(默认)/ 自定义字符串。client(出站加密/签名)与 sandbox(入站解密/验签)是同一协议的一对镜像,格式在声明中指定一次,两边的生成骨架都读它。
|
|
49
|
+
- 方法级 `service`(可选):发往网关的 **wire service 名**(如方法 key `uploadImage` → `service: 'pic_upload'`),默认 = 方法 key。camelCase key 与 snake_case wire 名不一致时必须显式声明——sandbox endpoint 的路由靠它匹配。
|
|
50
|
+
- `description`(可选):服务或方法说明。
|
|
51
|
+
|
|
52
|
+
字段与本地实体列/其他消息字段的关系,两个通道,按"同一事实"的表达方式选择:
|
|
53
|
+
|
|
54
|
+
### 同一概念且类型一致 → 共享实例
|
|
55
|
+
|
|
56
|
+
直接复用本地实体列实例,类型/语义/默认值自动跟随实体,DTO 投影继承全部语义:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
args: {
|
|
60
|
+
name: 'PayParams',
|
|
61
|
+
fields: {
|
|
62
|
+
openid: dtoField(user.columns.openid), // user 是 TableSchema 实例,此处复用其 openid 列的 Field 对象
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
线格式字段名(map key)与列名无需一致——key 是协议拼写,value 是任意 Field 实例,两者解耦。协议叫 `userId`、列叫 `user_id` 照样共享:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
fields: {
|
|
71
|
+
userId: dtoField(user.columns.user_id), // key 按协议拼写,value 复用列实例
|
|
72
|
+
},
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> `user` 是 `schema/user.table.ts` 中 `export const user = defineTable('user', { ... })` 导出的 **TableSchema 实例**(`user.columns` 是它的列 map,`user.columns.openid` 是该表 `openid` 列的 Field 实例)。共享实例即把**同一个 Field 对象**放入消息字段,类型/语义/默认值全部跟随表定义。
|
|
76
|
+
|
|
77
|
+
### 同一概念但类型/格式不同 → 自有字段
|
|
78
|
+
|
|
79
|
+
声明自有线格式类型:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const totalFee = intField({ optional: false, label: '金额(分)' });
|
|
83
|
+
|
|
84
|
+
fields: {
|
|
85
|
+
total_fee: dtoField(totalFee),
|
|
86
|
+
},
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
wire 字段与本地字段之间的换算/映射(分↔元、加密、脱敏)由 convert 防腐层承载,`FieldRuleSchema` 换算规则为规划能力、尚未接入消息绑定。
|
|
90
|
+
|
|
91
|
+
## 嵌套字段
|
|
92
|
+
|
|
93
|
+
线格式字段支持递归嵌套,用 `objectField` / `arrayField`(Field 体系,非表列):
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
fields: {
|
|
97
|
+
payer_info: dtoField(objectField({
|
|
98
|
+
properties: {
|
|
99
|
+
openid: stringField({ optional: false, maxLength: 64 }),
|
|
100
|
+
},
|
|
101
|
+
})),
|
|
102
|
+
coupons: dtoField(arrayField({ items: intField() })),
|
|
103
|
+
},
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
表列不支持这两个类型(`buildCreateTableSql` 直接报错,定义期即拦截)。
|
|
107
|
+
|
|
108
|
+
## 枚举与异常
|
|
109
|
+
|
|
110
|
+
第三方消息字段可用 `enumField` 挂 `defineEnum` 枚举,两者与 `defineThirdService` 定义在同一源文件中(named export),供 `pylonts gen third` 生成枚举产物。
|
|
111
|
+
|
|
112
|
+
异常不在此处定义——方法 `throws` 固定声明 `@pylonts/core` 的 `CodeException` + `IOException`(见上文「定义」一节),`pylonts gen third` 强校验。
|
|
113
|
+
|
|
114
|
+
## 回调(callbacks,第三方 → 平台推送)
|
|
115
|
+
|
|
116
|
+
`defineThirdService` 的 `callbacks` 字段建模**入站推送**:第三方审核完成/状态变更后主动 POST 到平台(如银联自助签约 3.8 入网审核结果推送、3.12 变更推送)。与 `methods`(平台 → 第三方出站)方向相反,语义是"第三方发来报文,平台应答"。
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { buildInput, buildOutput, defineThirdCallback, defineThirdService, dtoField, stringField } from '@pylonts/dsl';
|
|
120
|
+
import { api, unionpay } from '../project.config';
|
|
121
|
+
|
|
122
|
+
export const unionpaySigningService = defineThirdService({
|
|
123
|
+
schema: unionpay,
|
|
124
|
+
name: 'UnionpaySigningService',
|
|
125
|
+
methods: {
|
|
126
|
+
// ... 出站方法(3.1 图片上传等)
|
|
127
|
+
},
|
|
128
|
+
callbacks: {
|
|
129
|
+
applyNotify: defineThirdCallback({
|
|
130
|
+
api, // 生成到 api/src/modules/unionpay/controller/
|
|
131
|
+
service: 'apply_notify', // 第三方推送的 service 名(固定传输值)
|
|
132
|
+
payload: buildInput('ApplyNotifyPayload', {
|
|
133
|
+
ums_reg_id: dtoField(stringField({ maxLength: 30, optional: false })),
|
|
134
|
+
apply_status: dtoField(enumField({ enum: ApplyStatus, optional: true })),
|
|
135
|
+
// ... 推送报文字段
|
|
136
|
+
}),
|
|
137
|
+
response: buildOutput('NotifyResponse', {
|
|
138
|
+
res_code: dtoField(stringField({ maxLength: 10, optional: false, label: '0000 成功' })),
|
|
139
|
+
}),
|
|
140
|
+
description: '入网审核结果推送',
|
|
141
|
+
}),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `callbacks` 是 **map**:key 即回调方法名(构建器写回 `callback.name`),value 由 **`defineThirdCallback`** 构建。
|
|
147
|
+
- **`api`(必填)**:回调 controller 生成进哪个后端——`{api}/src/modules/{thirdApi.name}/controller/{ServiceName}CallbackController.ts`。这是 **@Public 白名单目录**(`lint controller`:`src/modules/{thirdApi}/controller/` 下允许 @Public,thirdApi 名来自 project.config.ts)——第三方调用不持有平台 appKey,平台在 controller 方法体内验第三方的自有签名。
|
|
148
|
+
- **`service`(必填)**:第三方推送的服务名(如 `apply_notify`、`alter_notify`),供回调实现按 service 分发。
|
|
149
|
+
- **`payload` / `response`**:各是一个 `DtoMessage`(`buildInput`/`buildOutput`),与 `methods` 的 args/results 同一套 POJO 聚合,wire 字段名原样保留协议拼写;`payload` 是第三方推来的报文,`response` 是平台应答报文(如 `{ res_code: '0000' }`)。
|
|
150
|
+
- `description`(可选):回调说明,渲染为 `@Response` 的 OpenAPI 描述。
|
|
151
|
+
|
|
152
|
+
生成物(`pylonts gen third` 一并产出):
|
|
153
|
+
|
|
154
|
+
| 产物 | 路径 | 性质 |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| payload/response TypeBox 消息 | `third/{thirdApi}/{stem}.third-service.gen.ts`(与出站 DTO 同文件) | 覆盖写 |
|
|
157
|
+
| 回调 controller 骨架 | `{api}/src/modules/{thirdApi}/controller/{ServiceName}CallbackController.ts` | 已存在合并(用户方法体保留),`--force` 覆盖 |
|
|
158
|
+
|
|
159
|
+
回调 controller 是 `@Rpc('{serviceStem}Callback')` + 每方法 `@Public()` + `@Body(payload)` + `@Response(desc, response)` 的 RPC 类;**方法体是手写面**:验第三方签名/解密(与出站 client 的密钥对称)、按 `service` 分发、落库/通知业务,最后返回 `response` 消息。RPC 路由为 `POST /{urlPrefix}/{rpcName}/{method}`,第三方按文档推送即可。
|
|
160
|
+
|
|
161
|
+
## 存储与生成
|
|
162
|
+
|
|
163
|
+
- 声明:`third_schema/{thirdApi.name}/*.third-service.ts`——一文件一服务(named export),目录名 = project.config.ts 的 `thirdApis` 实例名。
|
|
164
|
+
- 生成:`pylonts gen third`——对每个 thirdApi,扫描 `third_schema/{name}/`,按三步产出到 `third/{name}/`:
|
|
165
|
+
0. **throws 校验**(生成前置闸门):每个方法必须声明 `CodeException` + `IOException`,缺失即报错列出违规方法,不写任何产物;
|
|
166
|
+
1. **枚举**:模块导出的 `defineEnum` 实例 → `third/{name}/enums/{JsName}.enum.ts`(复用 enum-driver 的 `renderEnum`,与表枚举同一渲染),一 jsName 一文件;
|
|
167
|
+
2. **DTO**:每方法 args/results 用 typebox-driver 渲染 TypeBox 消息 + Static 类型,**一源文件一生成文件**,输出 `third/{name}/{stem}.third-service.gen.ts`(覆盖写);
|
|
168
|
+
3. **客户端骨架**:每服务渲染一个 class(构造配置接口 + 每方法 async 签名 + Not-implemented throw),输出 `third/{name}/{stem}.client.ts`——已存在则**合并**(用户填充的方法体保留、签名/DTO import 刷新),`--force` 覆盖;
|
|
169
|
+
4. **沙箱网关配置**(可选):每 thirdApi 渲染 `sandbox/{name}/config.ts`(defineSandboxConfig 骨架)——每方法一个 endpoint(`service` 名 = 声明的 wire service、`format` = 声明的数据格式、`method` 默认 POST、handler stub `res_code: '0000'`),已存在则**合并**(用户填充的 handler 保留、契约新增接口自动追加、移除接口删除),`--force` 覆盖;**无生成标记的手写 config 保留不动**(cli 打印 `- (hand-written, delete to regenerate)` 提示)。encoder(解密/验签)与 GATEWAY baseUrl 是手写面,不在生成范围。
|
|
170
|
+
- DTO 的枚举字段 import 走**相对路径** `./enums/{JsName}.enum`(DTO 与 enums/ 同处 `third/{name}/` 下,`moduleResolution: bundler` 解析 `.enum.ts`),不依赖根 `enums/` 子包。
|
|
171
|
+
- 生成物目录是子包:`third/` 目录带 `package.json`,`exports` 声明 `*.third-service.gen` 子路径,供 convert 产物 import。
|
|
172
|
+
|
|
173
|
+
## 客户端骨架是生成的,方法体是手写的
|
|
174
|
+
|
|
175
|
+
`defineThirdService` 只描述**契约**(构造配置 + 方法列表)。`gen third` 生成的 `{name}.client.ts` 是一个**骨架**:导出 `{Service}Config` 接口(TODO 注释标注 transport 配置——baseUrl/凭据/密钥属外部实现,不在契约内)+ `{Service}` 类(constructor 空实现),每方法带完整签名(args/results 类型从同名 `.third-service.gen` import type)与 `throw new Error('Not implemented: ...')` stub(含 `// @gen:stub` 标记,与 gen-service 骨架同一套 marker 约定)。**签名/throws 注释由生成器保证与契约同步,方法体、构造配置、签名加密等外部交互由人工填充**——已存在文件默认跳过(避免覆盖人工实现),`--force` 才重写。
|
|
176
|
+
|
|
177
|
+
## convert 防腐接线
|
|
178
|
+
|
|
179
|
+
第三方消息(`args`/`results` 是 `DtoMessage`,天然满足 `ConvertSourceSchema`)可直接作为 convert 的**源或目标**,用于 wire 消息 ↔ 本地模型的防腐翻译。```ts
|
|
180
|
+
// convert_schema/{api.name}/{app.name}/convert/wx-pay.convert.ts
|
|
181
|
+
import { wxPayService } from '../../../third_schema/wx/wxpay.third-service';
|
|
182
|
+
|
|
183
|
+
const getPayParams = wxPayService.methods.getPayParams;
|
|
184
|
+
|
|
185
|
+
export const wxPayConvert = defineConvert({
|
|
186
|
+
name: 'WxPayConvert',
|
|
187
|
+
api,
|
|
188
|
+
app: admin,
|
|
189
|
+
methods: {
|
|
190
|
+
toPayParams: {
|
|
191
|
+
sources: [order], // 本地订单表 → wire 请求
|
|
192
|
+
target: getPayParams.args,
|
|
193
|
+
},
|
|
194
|
+
toLocalPayResult: {
|
|
195
|
+
sources: [getPayParams.results], // wire 响应 → 本地 DTO
|
|
196
|
+
target: WxPayParamsResultDto,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
152
202
|
convert 文件绑定第三方服务身份时按 `{third-service}.convert.ts` 命名(上例 `wx-pay.convert.ts` 对应 `wxpay.third-service.ts` 的 `WxPayService`)。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# HMAC 签名 + JWT → token 令牌体系迁移对照
|
|
2
|
+
|
|
3
|
+
> 状态:**已落地**(token 体系为唯一鉴权体系,旧体系完全退役)
|
|
4
|
+
> 关联:[token.md](token.md)(体系总文档)、`pylon-fastify/src/common/auth/TokenStrategy.ts`(auth 链实现)
|
|
5
|
+
|
|
6
|
+
旧体系是**两个互不相关的层**:HMAC 签名层证明"请求来自合法客户端",JWT 登录层证明"用户是谁"——两条路径、两套密钥、两套过期与吊销管理。token 体系把它们合并为一个**两态对象**:签名材料(security 段)与身份数据(identity 段)统一由 Redis 平面对象承载,验签与认证一次完成。
|
|
7
|
+
|
|
8
|
+
## 组件对照
|
|
9
|
+
|
|
10
|
+
| 旧体系(HMAC + JWT) | token 体系 | 说明 |
|
|
11
|
+
|---------------------|-----------|------|
|
|
12
|
+
| `sign/issue` + `sign/refresh` 凭证引导端点(SignController) | **退役** | 无公共签发接口;每个 app 的登录入口(`@LoginEntry` 标记的 login + refresh 双端点)即入口 |
|
|
13
|
+
| `schema/api_keys.table.ts`(appKey/appSecret 存储) | **退役** | 会话凭据(token + refresh_token + login_at)存账户表(身份表硬约束三列) |
|
|
14
|
+
| HMAC 签名头:`x-app-key` / `x-timestamp` / `x-nonce` / `x-signature` | `x-token` / `x-timestamp` / `x-nonce` / `x-signature` | `x-app-key` 换成 `x-token`;timestamp + nonce 防重放**保留**;签名串格式不变(method + path + timestamp + nonce + canonical body) |
|
|
15
|
+
| 签名密钥:按 appKey 查密钥表(sign-db-driver / sign-redis-driver) | Redis 对象 secret 字段(`{app}.{token}` 一次还原) | 验签材料与身份同对象,零额外查询 |
|
|
16
|
+
| JWT:`Authorization: Bearer <token>`,payload 携带 user 对象 | token 纯随机 hash(`x-token` 头) | 不可解析、无 payload/exp,客户端只见 hash |
|
|
17
|
+
| `signToken` / `verifyToken` / blacklist 检查 | **退役** | 删 Redis key 即吊销,blacklist 补丁不再需要 |
|
|
18
|
+
| JWT secret 一套密钥 | secret/cipher 随 token 对象存 Redis(login 响应下发 secret) | 每会话独立密钥,轮换随 token 走 |
|
|
19
|
+
| `@Public()`(sign/issue 唯一豁免 + 第三方回调) | **退役,唯一例外:第三方回调** | 所有接口必须签名无豁免;第三方回调 controller(thirdApis 名下模块)是唯一 `@Public` 白名单(lint 强制) |
|
|
20
|
+
| `@Login('<模块名>')` + `user.type` 403 判定 | `@Login('<app>')` 语义保留 | module = app 名,兼作 Redis key 前缀(app 命名空间隔离) |
|
|
21
|
+
| 无登录入口标记 | **`@LoginEntry(app?)`**(新增) | 标记登录入口 controller(login + refresh 双端点),auth 链据此 + body 是否带 refreshToken 区分验证模式 |
|
|
22
|
+
| `(body, user: User)` handler 第二参 | `(body, token: {Name}Token)` | User 退役;按 api+app 自动推导模块 token 类型(无 token 声明 = 生成报错) |
|
|
23
|
+
| `__inject` 只映射一个 id(`(body, user)`) | `fromToken(token, [...])` 多属性注入(`(body, token)`) | 服务器注入字段从 Token 平面对象填充 |
|
|
24
|
+
| refreshToken:无此概念(JWT 过期重新登录) | refreshToken 7 天滑动窗口 + 每次刷新轮换 | 只在 login 返回;**只在 refresh 请求体中上送**(不出现在任何 header) |
|
|
25
|
+
| 登录过期:JWT exp + 可选 blacklist | token 固定 30 分钟(Redis TTL)+ refresh 续期 | 删 token 即时终止会话;refresh 校验账号状态 + 7 天窗口 |
|
|
26
|
+
|
|
27
|
+
## 验证模式对照
|
|
28
|
+
|
|
29
|
+
| 旧体系(装饰器决定) | 鉴权 | 新体系(请求形态决定) | 签名密钥 |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `@Public()` | 无 | 第三方回调(唯一白名单) | 不签名(第三方自身验签手写在 controller) |
|
|
32
|
+
| 无装饰器(默认) | HMAC 签名 | 业务请求(带 `x-token`) | Redis 对象 secret |
|
|
33
|
+
| `@Login('<模块>')` | HMAC 签名 + JWT | 业务请求 + app scope | 同上 |
|
|
34
|
+
| — | — | login 入口(`@LoginEntry`,body 无 refreshToken) | bootstrap 初始密钥(固定密钥 + 时间窗口,两端独立计算) |
|
|
35
|
+
| — | — | refresh 入口(`@LoginEntry`,body 带 refreshToken) | refreshToken 派生密钥(`HMAC(refreshToken, 固定盐)`,能签名 == 持有 refreshToken) |
|
|
36
|
+
|
|
37
|
+
## 端点对照
|
|
38
|
+
|
|
39
|
+
| 旧体系 | token 体系 |
|
|
40
|
+
|--------|-----------|
|
|
41
|
+
| `POST sign/issue` → appKey + appSecret(存 api_keys 表) | 无(客户端用 bootstrap 初始密钥签名 login) |
|
|
42
|
+
| `POST sign/refresh` → 新 appSecret | 无(refreshToken 轮换内建于 login 链) |
|
|
43
|
+
| `POST login/login` → JWT | `POST {app}/login/login` → token + refreshToken + secret(`@LoginEntry`) |
|
|
44
|
+
| 无独立 refresh 端点 | `POST {app}/login/refresh` → 新 token + 新 refreshToken(body 上送 refreshToken,`@LoginEntry`) |
|
|
45
|
+
|
|
46
|
+
## 客户端对照
|
|
47
|
+
|
|
48
|
+
| 旧体系 | token 体系 |
|
|
49
|
+
|--------|-----------|
|
|
50
|
+
| `createCredentialFetcher` + `createApiClient`(先取 appKey 再签名) | token 会话客户端:login 后持有 token + secret 自动签名;token 过期**内部自动 refresh**(固定流程,不暴露公共 refresh 方法、不生成前端 refresh 调用函数) |
|
|
51
|
+
| 每次请求带 `x-app-key` + HMAC 签名 + `Authorization` | 每次请求带 `x-token` + 签名(secret 取自会话) |
|
|
52
|
+
|
|
53
|
+
## 迁移清单(旧项目 → token 体系)
|
|
54
|
+
|
|
55
|
+
1. **删 SignController / sign 链**:`sign/issue`、`sign/refresh` 端点、`schema/api_keys.table.ts`、sign-db/sign-redis-driver 接线全部移除。
|
|
56
|
+
2. **`@Public` 清理**:除 thirdApis 名下回调 controller 外一律删除(lint 白名单强制)。
|
|
57
|
+
3. **登录链重建**:`token_schema/{api}/{app}/token/*.token.ts` 定义身份(identity 投影身份表,硬约束三列)→ `pylonts gen token` → `login.config.ts` 引用 token → `pylonts gen login`。
|
|
58
|
+
4. **auth 链配置**:`AppConfig.auth.token`——`resolveToken`(Redis 还原)、`bootstrapSecrets`(app → 初始密钥)、`refreshKeySalt`(refreshToken 派生盐)、nonceStore。
|
|
59
|
+
5. **controller 第二参**:`(body, user: User)` → `(body, token: {Name}Token)`——`pylonts gen controller` 自动按 api+app 推导,app 无 token 声明会报错(补齐声明)。
|
|
60
|
+
6. **JWT 依赖清理**:`signToken` / `verifyToken` / blacklist / `Authorization` 解析全部移除。
|