qing-client 0.0.2
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/lib/client/BaseClient.d.ts +21 -0
- package/lib/client/BaseClient.js +194 -0
- package/lib/client/index.d.ts +14 -0
- package/lib/client/index.js +24 -0
- package/lib/index.d.ts +0 -0
- package/lib/index.js +1 -0
- package/lib/srvice/AuthService.d.ts +8 -0
- package/lib/srvice/AuthService.js +45 -0
- package/lib/srvice/MsgService.d.ts +8 -0
- package/lib/srvice/MsgService.js +26 -0
- package/lib/srvice/TokenService.d.ts +10 -0
- package/lib/srvice/TokenService.js +42 -0
- package/lib/srvice/UserService.d.ts +10 -0
- package/lib/srvice/UserService.js +61 -0
- package/lib/types/index.d.ts +44 -0
- package/lib/types/index.js +2 -0
- package/lib/types/msg.d.ts +31 -0
- package/lib/types/msg.js +2 -0
- package/lib/types/token.d.ts +22 -0
- package/lib/types/token.js +2 -0
- package/lib/types/user.d.ts +37 -0
- package/lib/types/user.js +2 -0
- package/package.json +51 -0
- package/src/client/BaseClient.ts +230 -0
- package/src/client/index.ts +27 -0
- package/src/index.ts +0 -0
- package/src/srvice/AuthService.ts +50 -0
- package/src/srvice/MsgService.ts +28 -0
- package/src/srvice/TokenService.ts +66 -0
- package/src/srvice/UserService.ts +74 -0
- package/src/types/index.ts +58 -0
- package/src/types/msg.ts +32 -0
- package/src/types/token.ts +31 -0
- package/src/types/user.ts +43 -0
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qing-client",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"main": "lib/index.js",
|
|
5
|
+
"types": "lib/index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"coverage": "jest --coverage",
|
|
9
|
+
"tree": "tree -l 10 -o tree-out.txt --ignore node_modules/ --ignore build/ --ignore dist/ --ignore migrations/",
|
|
10
|
+
"lint": "eslint",
|
|
11
|
+
"dev": "ts-node-dev src/index.ts",
|
|
12
|
+
"t": "ts-node-dev src/test/testRunner.ts",
|
|
13
|
+
"build": "tsc -p tsconfig.build.json",
|
|
14
|
+
"rele": "bash release.sh"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@faker-js/faker": "^10.0.0",
|
|
21
|
+
"@types/formidable": "^3.4.5",
|
|
22
|
+
"@types/jest": "^30.0.0",
|
|
23
|
+
"@types/node": "^24.3.1",
|
|
24
|
+
"@types/qs": "^6.14.0",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^8.43.0",
|
|
26
|
+
"@typescript-eslint/parser": "^8.43.0",
|
|
27
|
+
"eslint": "^8.2.0",
|
|
28
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
29
|
+
"eslint-plugin-import": "^2.32.0",
|
|
30
|
+
"eslint-plugin-node": "^11.1.0",
|
|
31
|
+
"jest": "^30.1.3",
|
|
32
|
+
"ts-jest": "^29.4.1",
|
|
33
|
+
"ts-node": "^10.9.2",
|
|
34
|
+
"ts-node-dev": "^2.0.0",
|
|
35
|
+
"typescript": "^5.9.2"
|
|
36
|
+
},
|
|
37
|
+
"description": "",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"axios": "^1.11.0",
|
|
40
|
+
"qs": "^6.14.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"lib",
|
|
45
|
+
"src",
|
|
46
|
+
"types",
|
|
47
|
+
"package.json",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosRequestConfig, AxiosError, RawAxiosRequestHeaders } from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
UserContext,
|
|
4
|
+
ClientConfig,
|
|
5
|
+
RequestOptions,
|
|
6
|
+
TokenStorage,
|
|
7
|
+
ApiResponse,
|
|
8
|
+
PaginatedResponse
|
|
9
|
+
} from '../types';
|
|
10
|
+
|
|
11
|
+
// 默认内存存储实现
|
|
12
|
+
const memoryTokenStorage: TokenStorage & { token?: string } = {
|
|
13
|
+
token: undefined,
|
|
14
|
+
async getToken() {
|
|
15
|
+
return this.token;
|
|
16
|
+
},
|
|
17
|
+
async setToken(token: string) {
|
|
18
|
+
this.token = token;
|
|
19
|
+
},
|
|
20
|
+
async clearToken() {
|
|
21
|
+
this.token = undefined;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export abstract class BaseClient {
|
|
26
|
+
protected axiosInstance: AxiosInstance;
|
|
27
|
+
protected userContext?: UserContext;
|
|
28
|
+
protected isFrontendMode: boolean;
|
|
29
|
+
protected serviceBasePath: string;
|
|
30
|
+
protected tokenStorage: TokenStorage;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
protected config: ClientConfig,
|
|
34
|
+
protected serviceName: string
|
|
35
|
+
) {
|
|
36
|
+
this.isFrontendMode = !!config.gatewayUrl;
|
|
37
|
+
this.serviceBasePath = `/api/${serviceName}`;
|
|
38
|
+
|
|
39
|
+
// 设置令牌存储
|
|
40
|
+
this.tokenStorage = config.tokenStorage || memoryTokenStorage;
|
|
41
|
+
|
|
42
|
+
// 创建Axios实例
|
|
43
|
+
this.axiosInstance = axios.create({
|
|
44
|
+
baseURL: this.getBaseUrl(),
|
|
45
|
+
timeout: 30000,
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
'Accept': 'application/json'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 添加请求拦截器
|
|
53
|
+
this.axiosInstance.interceptors.request.use(async (config: any) => {
|
|
54
|
+
let headers: RawAxiosRequestHeaders = config.headers || {};
|
|
55
|
+
|
|
56
|
+
// 1. 注入用户上下文(后端模式)
|
|
57
|
+
if (!this.isFrontendMode && this.userContext) headers = {
|
|
58
|
+
...headers,
|
|
59
|
+
...this.initUserContext(this.userContext)
|
|
60
|
+
}
|
|
61
|
+
// 2. 注入认证令牌
|
|
62
|
+
const token = await this.tokenStorage.getToken();
|
|
63
|
+
if (token) {
|
|
64
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 合并自定义头
|
|
68
|
+
if (config.headers) {
|
|
69
|
+
Object.assign(headers, config.headers);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...config,
|
|
74
|
+
headers
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
initUserContext(context: UserContext): RawAxiosRequestHeaders {
|
|
80
|
+
return {
|
|
81
|
+
'v-user-id': context.userId,
|
|
82
|
+
'v-user-role': context.role,
|
|
83
|
+
'v-project-id': context.projectId
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 设置用户上下文(后端模式使用)
|
|
88
|
+
setUserContext(context: UserContext): this {
|
|
89
|
+
this.userContext = context;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 设置令牌存储(可在运行时更换存储策略)
|
|
94
|
+
setTokenStorage(storage: TokenStorage): this {
|
|
95
|
+
this.tokenStorage = storage;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 确定请求基础URL
|
|
100
|
+
private getBaseUrl(): string {
|
|
101
|
+
if (this.isFrontendMode) {
|
|
102
|
+
return this.config.gatewayUrl!;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 后端模式使用服务专属URL
|
|
106
|
+
switch (this.serviceName) {
|
|
107
|
+
case 'auth': return this.config.authServiceUrl!;
|
|
108
|
+
case 'msg': return this.config.msgServiceUrl!;
|
|
109
|
+
case 'users': return this.config.userServiceUrl!;
|
|
110
|
+
case 'file': return this.config.fileServiceUrl!;
|
|
111
|
+
case 'survey': return this.config.surveyServiceUrl!;
|
|
112
|
+
case 'token': return this.config.tokenServiceUrl!;
|
|
113
|
+
default: throw new Error(`Unsupported service: ${this.serviceName}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 设置认证令牌
|
|
118
|
+
async setToken(token: string): Promise<void> {
|
|
119
|
+
await this.tokenStorage.setToken(token);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 清除认证令牌
|
|
123
|
+
async clearToken(): Promise<void> {
|
|
124
|
+
await this.tokenStorage.clearToken();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 统一请求方法 - 处理标准API响应
|
|
128
|
+
protected async request<T>(
|
|
129
|
+
path: string,
|
|
130
|
+
options: RequestOptions = { method: 'GET' }
|
|
131
|
+
): Promise<T> {
|
|
132
|
+
try {
|
|
133
|
+
const fullPath = this.isFrontendMode
|
|
134
|
+
? `${this.serviceBasePath}${path}`
|
|
135
|
+
: path;
|
|
136
|
+
let headers: any = options.headers || {};
|
|
137
|
+
const context = options.userContext || this.userContext;
|
|
138
|
+
if (context) {
|
|
139
|
+
headers = { ...headers, ...this.initUserContext(context) };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const response = await this.axiosInstance.request<ApiResponse<T>>({
|
|
143
|
+
url: fullPath,
|
|
144
|
+
method: options.method,
|
|
145
|
+
headers,
|
|
146
|
+
params: options.params,
|
|
147
|
+
data: options.body
|
|
148
|
+
});
|
|
149
|
+
console.log('Request URL:', this.getBaseUrl() + fullPath);
|
|
150
|
+
console.log('Request params:', options.params);
|
|
151
|
+
// 检查业务成功状态
|
|
152
|
+
if (!response.data.success) {
|
|
153
|
+
throw new Error(response.data.message || `业务请求失败`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 返回实际数据
|
|
157
|
+
return response.data.data;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
// 明确返回错误处理
|
|
160
|
+
return this.handleApiError(error as AxiosError, path);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 分页请求方法 - 处理分页API响应
|
|
165
|
+
protected async paginatedRequest<T>(
|
|
166
|
+
path: string,
|
|
167
|
+
options: RequestOptions = { method: 'GET' }
|
|
168
|
+
): Promise<PaginatedResponse<T>> {
|
|
169
|
+
try {
|
|
170
|
+
const fullPath = this.isFrontendMode
|
|
171
|
+
? `${this.serviceBasePath}${path}`
|
|
172
|
+
: path;
|
|
173
|
+
|
|
174
|
+
const response = await this.axiosInstance.request<PaginatedResponse<T>>({
|
|
175
|
+
url: fullPath,
|
|
176
|
+
method: options.method,
|
|
177
|
+
headers: options.headers,
|
|
178
|
+
params: options.params,
|
|
179
|
+
data: options.body
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// 检查业务成功状态
|
|
183
|
+
if (!response.data.success) {
|
|
184
|
+
throw new Error(response.data.message || `业务请求失败`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 返回分页数据 - 直接返回整个data部分
|
|
188
|
+
return response.data
|
|
189
|
+
} catch (error) {
|
|
190
|
+
this.handleApiError(error as AxiosError, path);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 统一错误处理方法
|
|
195
|
+
private handleApiError(error: AxiosError, path: string): never {
|
|
196
|
+
let errorMessage = `[${this.serviceName}服务] `;
|
|
197
|
+
|
|
198
|
+
if (error.response) {
|
|
199
|
+
const responseData = error.response.data as any;
|
|
200
|
+
|
|
201
|
+
// 尝试从响应体中提取错误信息
|
|
202
|
+
if (responseData?.message) {
|
|
203
|
+
errorMessage += responseData.message;
|
|
204
|
+
} else if (responseData?.error) {
|
|
205
|
+
errorMessage += responseData.error;
|
|
206
|
+
} else {
|
|
207
|
+
errorMessage += `请求失败,状态码: ${error.response.status}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 添加详细错误信息(开发环境)
|
|
211
|
+
if (process.env.NODE_ENV === 'development') {
|
|
212
|
+
errorMessage += ` | 状态码: ${error.response.status}`;
|
|
213
|
+
if (error.response.headers?.['x-request-id']) {
|
|
214
|
+
errorMessage += ` | 请求ID: ${error.response.headers['x-request-id']}`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} else if (error.request) {
|
|
218
|
+
errorMessage += '服务器未响应';
|
|
219
|
+
} else {
|
|
220
|
+
errorMessage += error.message;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 添加路径信息
|
|
224
|
+
errorMessage += ` (路径: ${path})`;
|
|
225
|
+
|
|
226
|
+
// 明确抛出错误
|
|
227
|
+
throw new Error(errorMessage);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AuthService } from "../srvice/AuthService";
|
|
2
|
+
import { MsgService } from "../srvice/MsgService";
|
|
3
|
+
import { TokenService } from "../srvice/TokenService";
|
|
4
|
+
import { UserService } from "../srvice/UserService";
|
|
5
|
+
import { ClientConfig, UserContext } from "../types";
|
|
6
|
+
|
|
7
|
+
export class Client {
|
|
8
|
+
readonly auth: AuthService;
|
|
9
|
+
readonly msg: MsgService;
|
|
10
|
+
readonly user: UserService;
|
|
11
|
+
readonly token: TokenService;
|
|
12
|
+
|
|
13
|
+
constructor(protected config: ClientConfig) {
|
|
14
|
+
this.auth = new AuthService(config);
|
|
15
|
+
this.msg = new MsgService(config);
|
|
16
|
+
this.user = new UserService(config);
|
|
17
|
+
this.token = new TokenService(config);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setUserContext(context: UserContext): this {
|
|
21
|
+
this.auth.setUserContext(context);
|
|
22
|
+
this.msg.setUserContext(context);
|
|
23
|
+
this.user.setUserContext(context);
|
|
24
|
+
this.token.setUserContext(context);
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/index.ts
ADDED
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BaseClient } from "../client/BaseClient";
|
|
2
|
+
import { ClientConfig, RequestOptions } from "../types";
|
|
3
|
+
import qs from 'qs'
|
|
4
|
+
import { LoginResponse } from "../types/user";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export class AuthService extends BaseClient {
|
|
8
|
+
constructor(config: ClientConfig) {
|
|
9
|
+
super(config, 'auth');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 用户登录 - 匹配后端请求格式
|
|
13
|
+
async login(
|
|
14
|
+
identifier: string,
|
|
15
|
+
password: string,
|
|
16
|
+
projectId: number = 0,
|
|
17
|
+
options?: RequestOptions
|
|
18
|
+
): Promise<LoginResponse> {
|
|
19
|
+
const body = qs.stringify({
|
|
20
|
+
grant_type: "password",
|
|
21
|
+
username: identifier,
|
|
22
|
+
password
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return this.request<LoginResponse>('/login', {
|
|
26
|
+
...options,
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
...options?.headers,
|
|
30
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
31
|
+
},
|
|
32
|
+
params: {
|
|
33
|
+
project_id: projectId
|
|
34
|
+
},
|
|
35
|
+
body: body
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 用户登出 - 匹配后端请求格式
|
|
40
|
+
async logout(token: string, options?: RequestOptions): Promise<void> {
|
|
41
|
+
return this.request<void>('/logout', {
|
|
42
|
+
...options,
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
...options?.headers,
|
|
46
|
+
Authorization: `Bearer ${token}`
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseClient } from "../client/BaseClient";
|
|
2
|
+
import { ClientConfig, RequestOptions } from "../types";
|
|
3
|
+
import { FeishuMessage, MailRequest } from "../types/msg";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class MsgService extends BaseClient {
|
|
7
|
+
constructor(config: ClientConfig) {
|
|
8
|
+
super(config, 'msg');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 发送邮件 - 返回实际结果
|
|
12
|
+
async sendMail(request: MailRequest, options?: RequestOptions): Promise<void> {
|
|
13
|
+
return this.request<void>('/mail/send', {
|
|
14
|
+
...options,
|
|
15
|
+
method: 'POST',
|
|
16
|
+
body: request
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 发送飞书消息 - 返回实际结果
|
|
21
|
+
async sendFeishuMessage(message: FeishuMessage, options?: RequestOptions): Promise<void> {
|
|
22
|
+
return this.request<void>('/webhook/feishu/send', {
|
|
23
|
+
...options,
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body: message
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { BaseClient } from "../client/BaseClient";
|
|
2
|
+
import {
|
|
3
|
+
ClientConfig,
|
|
4
|
+
RequestOptions,
|
|
5
|
+
} from "../types";
|
|
6
|
+
import {
|
|
7
|
+
WxOfficialAccountTokenResponse,
|
|
8
|
+
WxJsapiTicketResponse,
|
|
9
|
+
WxSignatureRequest,
|
|
10
|
+
WxSignatureResponse,
|
|
11
|
+
WxMiniProgramTokenResponse
|
|
12
|
+
} from "../types/token";
|
|
13
|
+
|
|
14
|
+
export class TokenService extends BaseClient {
|
|
15
|
+
constructor(config: ClientConfig) {
|
|
16
|
+
super(config, 'token');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 获取微信公众号access_token
|
|
20
|
+
async getWxOfficialAccountToken(
|
|
21
|
+
appid: string,
|
|
22
|
+
options?: RequestOptions
|
|
23
|
+
): Promise<WxOfficialAccountTokenResponse> {
|
|
24
|
+
return this.request<WxOfficialAccountTokenResponse>('/wxh5/accesstoken', {
|
|
25
|
+
...options,
|
|
26
|
+
method: 'GET',
|
|
27
|
+
params: { appid }
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 获取微信公众号jsapi_ticket
|
|
32
|
+
async getWxJsapiTicket(
|
|
33
|
+
appid: string,
|
|
34
|
+
options?: RequestOptions
|
|
35
|
+
): Promise<WxJsapiTicketResponse> {
|
|
36
|
+
return this.request<WxJsapiTicketResponse>('/wxh5/jsapi_ticket', {
|
|
37
|
+
...options,
|
|
38
|
+
method: 'GET',
|
|
39
|
+
params: { appid }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 获取微信公众号签名
|
|
44
|
+
async getWxSignature(
|
|
45
|
+
signatureRequest: WxSignatureRequest,
|
|
46
|
+
options?: RequestOptions
|
|
47
|
+
): Promise<WxSignatureResponse> {
|
|
48
|
+
return this.request<WxSignatureResponse>('/wxh5/signature', {
|
|
49
|
+
...options,
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: signatureRequest
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 获取微信小程序access_token
|
|
56
|
+
async getWxMiniProgramToken(
|
|
57
|
+
appid: string,
|
|
58
|
+
options?: RequestOptions
|
|
59
|
+
): Promise<WxMiniProgramTokenResponse> {
|
|
60
|
+
return this.request<WxMiniProgramTokenResponse>('/wxmp/accesstoken', {
|
|
61
|
+
...options,
|
|
62
|
+
method: 'GET',
|
|
63
|
+
params: { appid }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { BaseClient } from "../client/BaseClient";
|
|
2
|
+
import {
|
|
3
|
+
ClientConfig,
|
|
4
|
+
RequestOptions,
|
|
5
|
+
PaginatedResponse
|
|
6
|
+
} from "../types";
|
|
7
|
+
import { User, UserCreateRequest, UserUpdateRequest } from "../types/user";
|
|
8
|
+
|
|
9
|
+
export class UserService extends BaseClient {
|
|
10
|
+
constructor(config: ClientConfig) {
|
|
11
|
+
super(config, 'users');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 获取当前用户信息 - 返回完整响应
|
|
15
|
+
async getCurrentUser(options?: RequestOptions): Promise<User> {
|
|
16
|
+
return this.request<User>('/me', {
|
|
17
|
+
...options,
|
|
18
|
+
method: 'GET'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 创建用户(管理员) - 匹配后端请求格式
|
|
23
|
+
async createUser(userData: UserCreateRequest, options?: RequestOptions): Promise<User> {
|
|
24
|
+
return this.request<User>('', {
|
|
25
|
+
...options,
|
|
26
|
+
method: 'POST',
|
|
27
|
+
body: {
|
|
28
|
+
username: userData.username,
|
|
29
|
+
email: userData.email,
|
|
30
|
+
password: userData.password,
|
|
31
|
+
name: userData.name,
|
|
32
|
+
role: userData.role,
|
|
33
|
+
phone: userData.phone,
|
|
34
|
+
project_id: userData.project_id
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 更新用户信息 - 匹配后端请求格式
|
|
40
|
+
async updateUser(userId: number, updateData: UserUpdateRequest, options?: RequestOptions): Promise<User> {
|
|
41
|
+
return this.request<User>(`/${userId}`, {
|
|
42
|
+
...options,
|
|
43
|
+
method: 'PUT',
|
|
44
|
+
body: {
|
|
45
|
+
name: updateData.name,
|
|
46
|
+
avatar: updateData.avatar,
|
|
47
|
+
phone: updateData.phone,
|
|
48
|
+
role: updateData.role,
|
|
49
|
+
project_id: updateData.project_id
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 获取用户列表(管理员) - 返回分页数据
|
|
55
|
+
async listUsers(
|
|
56
|
+
includeInactive: boolean = false,
|
|
57
|
+
page: number = 1,
|
|
58
|
+
perPage: number = 10,
|
|
59
|
+
options?: RequestOptions
|
|
60
|
+
): Promise<PaginatedResponse<User>> {
|
|
61
|
+
const response = await this.paginatedRequest<User>('', {
|
|
62
|
+
...options,
|
|
63
|
+
method: 'GET',
|
|
64
|
+
params: {
|
|
65
|
+
include_inactive: includeInactive,
|
|
66
|
+
page,
|
|
67
|
+
per_page: perPage
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 直接返回分页响应
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// 统一API响应格式
|
|
2
|
+
export interface ApiResponse<T = any> {
|
|
3
|
+
success: boolean;
|
|
4
|
+
message: string;
|
|
5
|
+
data: T;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 分页信息
|
|
9
|
+
export interface Pagination {
|
|
10
|
+
page: number;
|
|
11
|
+
per_page: number;
|
|
12
|
+
total: number;
|
|
13
|
+
total_pages: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 分页响应
|
|
17
|
+
export interface PaginatedResponse<T> {
|
|
18
|
+
data: T[];
|
|
19
|
+
success: boolean;
|
|
20
|
+
message: string;
|
|
21
|
+
pagination: Pagination;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// 添加令牌存储接口
|
|
26
|
+
export interface TokenStorage {
|
|
27
|
+
getToken(): Promise<string | undefined>;
|
|
28
|
+
setToken(token: string): Promise<void>;
|
|
29
|
+
clearToken(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 扩展客户端配置
|
|
33
|
+
export interface ClientConfig {
|
|
34
|
+
gatewayUrl?: string;
|
|
35
|
+
authServiceUrl?: string;
|
|
36
|
+
msgServiceUrl?: string;
|
|
37
|
+
userServiceUrl?: string;
|
|
38
|
+
fileServiceUrl?: string;
|
|
39
|
+
surveyServiceUrl?: string;
|
|
40
|
+
tokenServiceUrl?: string;
|
|
41
|
+
|
|
42
|
+
// 令牌存储策略
|
|
43
|
+
tokenStorage?: TokenStorage;
|
|
44
|
+
}
|
|
45
|
+
export interface UserContext {
|
|
46
|
+
userId: string;
|
|
47
|
+
role: 'SUPER_ADMIN' | 'ADMIN' | 'STAFF' | 'USER' | 'SYSTEM';
|
|
48
|
+
projectId: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface RequestOptions {
|
|
52
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
params?: Record<string, any>;
|
|
55
|
+
body?: any;
|
|
56
|
+
userContext?: UserContext;
|
|
57
|
+
}
|
|
58
|
+
|
package/src/types/msg.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface MailRequest {
|
|
2
|
+
to: string[];
|
|
3
|
+
subject: string;
|
|
4
|
+
text?: string;
|
|
5
|
+
html?: string;
|
|
6
|
+
from?: string;
|
|
7
|
+
cc?: string[];
|
|
8
|
+
bcc?: string[];
|
|
9
|
+
attachments?: Array<{
|
|
10
|
+
filename: string;
|
|
11
|
+
content: string; // base64编码
|
|
12
|
+
contentType?: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FeishuMessage {
|
|
17
|
+
url: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
bgColor?: string;
|
|
20
|
+
elements: Array<{
|
|
21
|
+
title: string;
|
|
22
|
+
text: string;
|
|
23
|
+
color?: string;
|
|
24
|
+
}>;
|
|
25
|
+
noticeUser?: Array<{
|
|
26
|
+
userId: string;
|
|
27
|
+
}>;
|
|
28
|
+
actions?: Array<{
|
|
29
|
+
text: string;
|
|
30
|
+
url: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// 微信公众号Token响应
|
|
2
|
+
export interface WxOfficialAccountTokenResponse {
|
|
3
|
+
access_token: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// 微信公众号JsapiTicket响应
|
|
7
|
+
export interface WxJsapiTicketResponse {
|
|
8
|
+
jsapi_ticket: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 微信公众号签名请求
|
|
12
|
+
export interface WxSignatureRequest {
|
|
13
|
+
appid: string;
|
|
14
|
+
url: string;
|
|
15
|
+
imgUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 微信公众号签名响应
|
|
19
|
+
export interface WxSignatureResponse {
|
|
20
|
+
appId: string;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
nonceStr: string;
|
|
23
|
+
signature: string;
|
|
24
|
+
url: string;
|
|
25
|
+
imgUrl: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 微信小程序Token响应
|
|
29
|
+
export interface WxMiniProgramTokenResponse {
|
|
30
|
+
access_token: string;
|
|
31
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// 用户模型
|
|
2
|
+
export interface User {
|
|
3
|
+
id: number;
|
|
4
|
+
username: string;
|
|
5
|
+
email: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
avatar?: string;
|
|
8
|
+
phone?: string;
|
|
9
|
+
role: string;
|
|
10
|
+
project_id: number;
|
|
11
|
+
is_active: boolean;
|
|
12
|
+
last_login_ip?: string;
|
|
13
|
+
last_login?: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
updated_at: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 用户创建请求
|
|
19
|
+
export interface UserCreateRequest {
|
|
20
|
+
username: string;
|
|
21
|
+
email: string;
|
|
22
|
+
password: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
role?: string;
|
|
25
|
+
phone?: string;
|
|
26
|
+
project_id?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 用户更新请求
|
|
30
|
+
export interface UserUpdateRequest {
|
|
31
|
+
name?: string;
|
|
32
|
+
avatar?: string;
|
|
33
|
+
phone?: string;
|
|
34
|
+
role?: string;
|
|
35
|
+
project_id?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface LoginResponse {
|
|
39
|
+
access_token: string;
|
|
40
|
+
token_type: string;
|
|
41
|
+
expires_at: string;
|
|
42
|
+
project_id: number;
|
|
43
|
+
}
|