apass-opensdk-hugong 1.0.5 → 1.0.6
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/apass/object_.d.ts +86 -0
- package/apass/object_.js +12 -12
- package/index.d.ts +90 -0
- package/index.js +85 -4
- package/opensdk/employee.js +16 -0
- package/package.json +2 -1
- package/readme.md +48 -14
- package/utils/date_.d.ts +35 -0
- package/utils/date_.js +9 -2
- package/utils/file_.d.ts +77 -0
- package/utils/file_.js +29 -9
- package/utils/index.d.ts +44 -0
- package/utils/index.js +43 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Object_.d.ts
|
|
2
|
+
export = Object_;
|
|
3
|
+
|
|
4
|
+
declare class Object_ {
|
|
5
|
+
constructor(hg: any);
|
|
6
|
+
|
|
7
|
+
/* ===== 查询 ===== */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 清空表中满足条件的数据
|
|
11
|
+
* @param table 表名
|
|
12
|
+
* @param where 条件对象;省略时全部清空
|
|
13
|
+
*/
|
|
14
|
+
clearData(table: string, where?: any): Promise<void>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 查询列表
|
|
18
|
+
* @param table 表名
|
|
19
|
+
* @param field 需要返回的字段,默认 ['_id','_name']
|
|
20
|
+
* @param where 过滤条件
|
|
21
|
+
* @param callback 每批记录回调;提供时方法返回 void
|
|
22
|
+
* @param orderby 排序字段,默认 '_id'
|
|
23
|
+
* @returns 无 callback 时返回全部记录
|
|
24
|
+
*/
|
|
25
|
+
findList<T = any>(
|
|
26
|
+
table: string,
|
|
27
|
+
field?: string[] | null,
|
|
28
|
+
where?: any,
|
|
29
|
+
callback?: (records: T[]) => Promise<void> | void,
|
|
30
|
+
orderby?: string
|
|
31
|
+
): Promise<T[] | void>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 查询单条记录
|
|
35
|
+
*/
|
|
36
|
+
findOne<T = any>(
|
|
37
|
+
table: string,
|
|
38
|
+
field?: string[] | null,
|
|
39
|
+
where?: any
|
|
40
|
+
): Promise<T | null>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 根据主键查询单条记录
|
|
44
|
+
*/
|
|
45
|
+
findOneById<T = any>(
|
|
46
|
+
table: string,
|
|
47
|
+
id: string | number,
|
|
48
|
+
field?: string[] | null
|
|
49
|
+
): Promise<T | null>;
|
|
50
|
+
|
|
51
|
+
/* ===== 写入 ===== */
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 批量创建
|
|
55
|
+
*/
|
|
56
|
+
batchCreate<T = any>(table: string, list: T[]): Promise<any>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 创建单条记录
|
|
60
|
+
*/
|
|
61
|
+
create<T = any>(table: string, data: T): Promise<any>;
|
|
62
|
+
|
|
63
|
+
/* ===== 更新 ===== */
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 批量更新
|
|
67
|
+
*/
|
|
68
|
+
batchUpdate<T = any>(table: string, list: T[]): Promise<any>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 更新单条记录
|
|
72
|
+
*/
|
|
73
|
+
update<T = any>(table: string, data: T): Promise<any>;
|
|
74
|
+
|
|
75
|
+
/* ===== 删除 ===== */
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 删除单条记录(按 _id)
|
|
79
|
+
*/
|
|
80
|
+
delete(table: string, _id: string | number): Promise<any>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 批量删除(按 _ids)
|
|
84
|
+
*/
|
|
85
|
+
delete(table: string, _ids: (string | number)[]): Promise<any>;
|
|
86
|
+
}
|
package/apass/object_.js
CHANGED
|
@@ -4,21 +4,21 @@ class Object_{
|
|
|
4
4
|
this.#hg = hg
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
async clearData(table,
|
|
8
|
-
await application.data.object(table).select('_id').where(
|
|
7
|
+
async clearData(table,where){
|
|
8
|
+
await application.data.object(table).select('_id').where( where ? where : {_id : application.operator.gte(0)}).findStream(async records=>{
|
|
9
9
|
this.#hg.log4('clear data len', records.length)
|
|
10
10
|
await application.data.object(table).batchDelete(records.map(it=>({_id: it._id})))
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
async findList(table,field,where,callback){
|
|
14
|
+
async findList(table,field,where,callback,orderby){
|
|
15
15
|
const list = []
|
|
16
|
-
await application.data.object(table).select(field || ['_id','_name']).where(where || {_id : application.operator.gte(0)}).findStream(async records=>{
|
|
16
|
+
await application.data.object(table).select(field || ['_id','_name']).where(where || {_id : application.operator.gte(0)}).orderByDesc(orderby || '_id').findStream(async records=>{
|
|
17
17
|
if(callback){
|
|
18
|
-
this.#hg.log4('find all len',
|
|
18
|
+
this.#hg.log4('find all len', records.length)
|
|
19
19
|
await callback(records)
|
|
20
20
|
}else{
|
|
21
|
-
this.#hg.log4('find all size',
|
|
21
|
+
this.#hg.log4('find all size', records.length)
|
|
22
22
|
list.push(...records)
|
|
23
23
|
}
|
|
24
24
|
})
|
|
@@ -36,30 +36,30 @@ class Object_{
|
|
|
36
36
|
|
|
37
37
|
async batchCreate(table,list){
|
|
38
38
|
if(list.length){
|
|
39
|
-
return await application.data.object(table).batchCreate(list)
|
|
39
|
+
return await application.data.object(table).useSystemAuth().batchCreate(list)
|
|
40
40
|
}
|
|
41
41
|
return null
|
|
42
42
|
}
|
|
43
43
|
async create(table,data){
|
|
44
|
-
return await application.data.object(table).create(data)
|
|
44
|
+
return await application.data.object(table).useSystemAuth().create(data)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async batchUpdate(table,list){
|
|
48
48
|
if(list.length){
|
|
49
|
-
return application.data.object(table).batchUpdate(list)
|
|
49
|
+
return application.data.object(table).useSystemAuth().batchUpdate(list)
|
|
50
50
|
}
|
|
51
51
|
return null
|
|
52
52
|
}
|
|
53
53
|
async update(table,data){
|
|
54
|
-
return application.data.object(table).update(data)
|
|
54
|
+
return application.data.object(table).useSystemAuth().update(data)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async delete(table,_id){
|
|
58
|
-
return application.data.object(table).delete(_id)
|
|
58
|
+
return application.data.object(table).useSystemAuth().delete(_id)
|
|
59
59
|
}
|
|
60
60
|
async delete(table,_ids){
|
|
61
61
|
if(_ids.length){
|
|
62
|
-
return application.data.object(table).batchDelete(_ids)
|
|
62
|
+
return application.data.object(table).useSystemAuth().batchDelete(_ids)
|
|
63
63
|
}
|
|
64
64
|
return null
|
|
65
65
|
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// HG.d.ts
|
|
2
|
+
export = HG;
|
|
3
|
+
|
|
4
|
+
declare class HG {
|
|
5
|
+
constructor(logger: any);
|
|
6
|
+
|
|
7
|
+
/* ── 公开属性 ──────────────────────────── */
|
|
8
|
+
// users: import('./opensdk/users');
|
|
9
|
+
// document: import('./opensdk/document');
|
|
10
|
+
// employee: import('./opensdk/employee');
|
|
11
|
+
// contract: import('./opensdk/contract');
|
|
12
|
+
object: import('./apass/object_');
|
|
13
|
+
utils: import('./utils/index');
|
|
14
|
+
readonly _time: number | null;
|
|
15
|
+
|
|
16
|
+
/* ── 公开方法 ──────────────────────────── */
|
|
17
|
+
setLogger(logger: any): Promise<void>;
|
|
18
|
+
initEnvAppId(): Promise<void>;
|
|
19
|
+
timeRun<T = any>(fn?: () => Promise<T>): Promise<T | null>;
|
|
20
|
+
sleep(time?: number): Promise<void>;
|
|
21
|
+
|
|
22
|
+
newTime(): void;
|
|
23
|
+
printTime(): void;
|
|
24
|
+
|
|
25
|
+
setAppId(app_id: string, app_secret: string): void;
|
|
26
|
+
getToken(): Promise<string>;
|
|
27
|
+
|
|
28
|
+
requestGet(url: string, params?: any, headers?: any): Promise<any>;
|
|
29
|
+
request(
|
|
30
|
+
url: string | [string, any],
|
|
31
|
+
data?: any,
|
|
32
|
+
headers?: any,
|
|
33
|
+
method?: string
|
|
34
|
+
): Promise<any>;
|
|
35
|
+
|
|
36
|
+
paginatedSearchGet(
|
|
37
|
+
url: string,
|
|
38
|
+
params?: any,
|
|
39
|
+
callback?: (items: any[]) => Promise<void> | void
|
|
40
|
+
): Promise<any[]>;
|
|
41
|
+
|
|
42
|
+
paginatedSearch(
|
|
43
|
+
url: string,
|
|
44
|
+
params?: any,
|
|
45
|
+
data?: any,
|
|
46
|
+
callback?: (items: any[]) => Promise<void> | void,
|
|
47
|
+
method?: string
|
|
48
|
+
): Promise<any[]>;
|
|
49
|
+
|
|
50
|
+
toMultilingual(
|
|
51
|
+
zh: string | any[],
|
|
52
|
+
en?: string
|
|
53
|
+
): any;
|
|
54
|
+
|
|
55
|
+
toText(textArr: Array<{ language_code: number; text: string }>): string;
|
|
56
|
+
|
|
57
|
+
toSafeValue<T = any>(
|
|
58
|
+
obj: Record<string, any> | null | undefined,
|
|
59
|
+
key: string,
|
|
60
|
+
defValue?: T
|
|
61
|
+
): T;
|
|
62
|
+
|
|
63
|
+
listFind<T = any>(
|
|
64
|
+
list: T[],
|
|
65
|
+
key: keyof T,
|
|
66
|
+
target: any,
|
|
67
|
+
defValue?: T
|
|
68
|
+
): T;
|
|
69
|
+
|
|
70
|
+
listMap<T = any>(list: T[], key: keyof T): any[];
|
|
71
|
+
|
|
72
|
+
textToFloat(textAmount?: string, defValue?: number): number;
|
|
73
|
+
|
|
74
|
+
toValue<T = any>(
|
|
75
|
+
obj: Record<string, any>,
|
|
76
|
+
path: string,
|
|
77
|
+
fallback?: T
|
|
78
|
+
): T;
|
|
79
|
+
|
|
80
|
+
/* ── 日志工具 ──────────────────────────── */
|
|
81
|
+
log(...args: any[]): void;
|
|
82
|
+
log4(...args: any[]): void;
|
|
83
|
+
log8(...args: any[]): void;
|
|
84
|
+
logm(space: number, ...args: any[]): void;
|
|
85
|
+
error(...args: any[]): void;
|
|
86
|
+
warn(...args: any[]): void;
|
|
87
|
+
|
|
88
|
+
/* ── 语言转换 ──────────────────────────── */
|
|
89
|
+
toLanguageByList(list: any[]): any[];
|
|
90
|
+
}
|
package/index.js
CHANGED
|
@@ -19,8 +19,14 @@ class HG{
|
|
|
19
19
|
this.utils = new Utils(this)
|
|
20
20
|
this._time = null
|
|
21
21
|
}
|
|
22
|
-
setLogger(logger){
|
|
22
|
+
async setLogger(logger){
|
|
23
23
|
this.#logger = logger
|
|
24
|
+
await this.initEnvAppId()
|
|
25
|
+
}
|
|
26
|
+
async initEnvAppId(){
|
|
27
|
+
this.#app_id = await application.globalVar.getVar('app_id')
|
|
28
|
+
this.#app_secret =await application.globalVar.getVar('app_secret')
|
|
29
|
+
this.log4(`app_id=${this.#app_id},app_secret=${this.#app_secret}`)
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* 运行函数
|
|
@@ -70,7 +76,7 @@ class HG{
|
|
|
70
76
|
* @param {*} app_id
|
|
71
77
|
* @param {*} app_secret
|
|
72
78
|
*/
|
|
73
|
-
|
|
79
|
+
setAppId(app_id,app_secret){
|
|
74
80
|
this.#app_id = app_id
|
|
75
81
|
this.#app_secret = app_secret
|
|
76
82
|
}
|
|
@@ -80,6 +86,9 @@ class HG{
|
|
|
80
86
|
* @returns
|
|
81
87
|
*/
|
|
82
88
|
async getToken(){
|
|
89
|
+
if(!this.#app_id){
|
|
90
|
+
await this.initEnvAppId()
|
|
91
|
+
}
|
|
83
92
|
const result = await this.request('https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal',{ app_id: this.#app_id, app_secret: this.#app_secret},false)
|
|
84
93
|
return result.tenant_access_token
|
|
85
94
|
}
|
|
@@ -177,14 +186,19 @@ class HG{
|
|
|
177
186
|
return new application.constants.type.Multilingual({ zh: _zh || _en, en: _en || _zh })
|
|
178
187
|
}
|
|
179
188
|
if(zh[0].hasOwnProperty('locale')){
|
|
180
|
-
const _zh = this.toSafeValue(zh.find(it=>it.locale == 'zh_CN'),'value')
|
|
181
|
-
const _en = this.toSafeValue(zh.find(it=>it.locale == 'en_US'),'value')
|
|
189
|
+
const _zh = this.toSafeValue(zh.find(it=>it.locale == 'zh_CN' || it.locale == 'zh-CN'),'value')
|
|
190
|
+
const _en = this.toSafeValue(zh.find(it=>it.locale == 'en_US' || it.locale == 'en-US'),'value')
|
|
182
191
|
return new application.constants.type.Multilingual({ zh: _zh || _en, en: _en || _zh })
|
|
183
192
|
}
|
|
184
193
|
}
|
|
185
194
|
return new application.constants.type.Multilingual({ zh: zh || en, en: en || zh })
|
|
186
195
|
}
|
|
187
196
|
|
|
197
|
+
toText(textArr){
|
|
198
|
+
if(!textArr || !textArr.length) return '--'
|
|
199
|
+
return textArr.find(it=>it.language_code == '2052').text
|
|
200
|
+
}
|
|
201
|
+
|
|
188
202
|
/**
|
|
189
203
|
* 安全的取值
|
|
190
204
|
* @param {*} obj
|
|
@@ -214,6 +228,18 @@ class HG{
|
|
|
214
228
|
return parseFloat((textAmount || '').replace(/,/g, '') || defValue);
|
|
215
229
|
}
|
|
216
230
|
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 从JSON中进行取值
|
|
234
|
+
* @param {*} obj
|
|
235
|
+
* @param {*} path
|
|
236
|
+
* @param {*} fallback
|
|
237
|
+
* @returns
|
|
238
|
+
*/
|
|
239
|
+
toValue(obj, path, fallback = undefined) {
|
|
240
|
+
return path.split('.').reduce((acc, key) => acc?.[key], obj) ?? fallback;
|
|
241
|
+
}
|
|
242
|
+
|
|
217
243
|
log(...arg){
|
|
218
244
|
this.#logger.log(...arg)
|
|
219
245
|
}
|
|
@@ -227,6 +253,61 @@ class HG{
|
|
|
227
253
|
const result = ''.padEnd(space, "-");
|
|
228
254
|
this.#logger.log(result,...arg)
|
|
229
255
|
}
|
|
256
|
+
error(...arg){
|
|
257
|
+
this.#logger.error(...arg)
|
|
258
|
+
}
|
|
259
|
+
warn(...arg){
|
|
260
|
+
this.#logger.warn(...arg)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
toLanguageByList(list){
|
|
264
|
+
// 定义语言代码映射
|
|
265
|
+
const languageMap = {
|
|
266
|
+
1033: "en_us",
|
|
267
|
+
2052: "zh_cn"
|
|
268
|
+
};
|
|
269
|
+
// 递归处理函数
|
|
270
|
+
const processJson = (data) => {
|
|
271
|
+
const result = [];
|
|
272
|
+
|
|
273
|
+
data.forEach(item => {
|
|
274
|
+
const newItem = {};
|
|
275
|
+
for (const key in item) {
|
|
276
|
+
const value = item[key];
|
|
277
|
+
if (Array.isArray(value)) {
|
|
278
|
+
// 如果是数组,处理数组中的每个对象
|
|
279
|
+
const newList = value.map(entry => {
|
|
280
|
+
if (entry.language_code in languageMap) {
|
|
281
|
+
return { [languageMap[entry.language_code]]: entry.text };
|
|
282
|
+
}
|
|
283
|
+
return null; // 如果 language_code 不在映射中,则忽略
|
|
284
|
+
}).filter(Boolean); // 过滤掉 null 值
|
|
285
|
+
if (newList.length > 0) {
|
|
286
|
+
newItem[key] = newList.reduce((obj,item)=>({...obj, ...item}),{});
|
|
287
|
+
}
|
|
288
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
289
|
+
// 如果是对象,递归处理
|
|
290
|
+
const processedValue = processJson([value])[0];
|
|
291
|
+
//console.log('processedValue',processedValue)
|
|
292
|
+
if (Object.keys(processedValue).length > 0) {
|
|
293
|
+
newItem[key] = processedValue;
|
|
294
|
+
}
|
|
295
|
+
}else{
|
|
296
|
+
newItem[key] = value
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (Object.keys(newItem).length > 0) {
|
|
300
|
+
result.push(newItem);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return result;
|
|
304
|
+
};
|
|
305
|
+
// 调用处理函数
|
|
306
|
+
const processedData = processJson(list);
|
|
307
|
+
return processedData
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
230
311
|
|
|
231
312
|
}
|
|
232
313
|
module.exports = HG
|
package/opensdk/employee.js
CHANGED
|
@@ -14,5 +14,21 @@ class Employee{
|
|
|
14
14
|
return await this.#hg.paginatedSearch(`https://open.feishu.cn/open-apis/corehr/v2/employees/search`,params,data,callback)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* 批量查询员工薪资档案
|
|
19
|
+
* @param {*} _id
|
|
20
|
+
* @param {*} user_id
|
|
21
|
+
*/
|
|
22
|
+
async archives_query(effective_start_date,effective_end_date,user_id_list,user_id_type){
|
|
23
|
+
return await this.#hg.paginatedSearch(`https://open.feishu.cn/open-apis/compensation/v1/archives/query`, {
|
|
24
|
+
page_size,
|
|
25
|
+
user_id_type: 'people_corehr_id',
|
|
26
|
+
},{
|
|
27
|
+
user_id_list,
|
|
28
|
+
effective_start_date,
|
|
29
|
+
effective_end_date,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
}
|
|
18
34
|
module.exports = Employee
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -23,13 +23,32 @@ apass-opensdk-hugong@1.0.3:
|
|
|
23
23
|
version "1.0.3"
|
|
24
24
|
resolved "https://registry.npmmirror.com/apass-opensdk-hugong/-/apass-opensdk-hugong-1.0.3.tgz#ef2d0a19e793358784cb764de4b885fd86befd66"
|
|
25
25
|
integrity sha512-Q8Gpo9v8a/2G43y/Lh2POyDEOcealFuUlGdqGSA9wyfjET4+LWs/PnzK9EB1YZCjQotW7cGO/9a0MiQ6Sgw1LA==
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
======
|
|
29
|
+
package.json
|
|
30
|
+
{
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"apass-opensdk-hugong": "1.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//改成
|
|
36
|
+
{
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"apass-opensdk-hugong": "1.0.5"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
部署后刷新页面即可生效
|
|
26
42
|
```
|
|
27
43
|
|
|
28
44
|
## 如何使用
|
|
29
45
|
```
|
|
30
46
|
const Hugong = require('apass-opensdk-hugong');
|
|
31
|
-
|
|
47
|
+
初始化(主函数内),函数外不需要入参logger
|
|
32
48
|
const hg = new Hugong(logger)
|
|
49
|
+
|
|
50
|
+
如果在外部初始化后需要在函数中初始化logger模块
|
|
51
|
+
hg.setLogger(logger)
|
|
33
52
|
```
|
|
34
53
|
## 常用
|
|
35
54
|
```
|
|
@@ -37,17 +56,26 @@ const hg = new Hugong(logger)
|
|
|
37
56
|
hg.newTime()
|
|
38
57
|
hg.printTime()
|
|
39
58
|
|
|
59
|
+
安全的取值
|
|
60
|
+
hg.toValue({....},'a.b.c','default value')
|
|
61
|
+
|
|
40
62
|
线程睡眠(毫秒)
|
|
41
63
|
await hg.sleep(2000)
|
|
42
64
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
数组操作
|
|
66
|
+
1)生成指定范围的数组
|
|
67
|
+
const rangeValue = hg.utils.range(5,50, 3)
|
|
68
|
+
out=[ 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50 ]
|
|
69
|
+
2)数组分块
|
|
70
|
+
const chunkValue = hg.utils.chunkAll(rangeValue, 5)
|
|
71
|
+
out=[ [ 5, 8, 11, 14, 17 ], [ 20, 23, 26, 29, 32 ], [ 35, 38, 41, 44, 47 ], [ 50 ] ]
|
|
72
|
+
数组分块输出1
|
|
73
|
+
hg.utils.splitArray([....], 5) 输出 [ [50],[50] ]
|
|
74
|
+
out=[ [ 5, 8, 11, 14, 17 ], [ 20, 23, 26, 29, 32 ], [ 35, 38, 41, 44, 47 ], [ 50 ] ]
|
|
75
|
+
数组分块输出2,每次输出50条 处理完成后继续下次执行
|
|
76
|
+
await hg.utils.splitArray([...], 10, async (items)=>{ // do something })
|
|
77
|
+
移除重复项
|
|
78
|
+
hg.utils.unique([1,2,3,1,2,3]) // [1,2,3]
|
|
51
79
|
|
|
52
80
|
```
|
|
53
81
|
## 文件上传/下载
|
|
@@ -61,18 +89,25 @@ await hg.utils.file.downloadFileToUpload(url,{ Authorization: `...`})
|
|
|
61
89
|
saveDataToEnv(data,path)
|
|
62
90
|
|
|
63
91
|
1)从飞书租户空间下载文件到本地环境中
|
|
64
|
-
file_info={ id, mime_type,
|
|
65
|
-
file_path=存储地址可选,不填写则默认当前时间戳
|
|
92
|
+
file_info={ id, mime_type, ...}
|
|
93
|
+
file_path=存储地址可选,不填写则默认当前时间戳
|
|
66
94
|
await hg.utils.file.downloadFileToTmp(fileInfo,file_path)
|
|
95
|
+
如果是token文件
|
|
96
|
+
file_info={ token, mime_type, ...}
|
|
97
|
+
await hg.utils.file.downloadFileByToeknToTmp(file_info,file_path)
|
|
67
98
|
|
|
68
99
|
2)解析csv文件,file_path必须是本地环境的文件路径 /tep/aaa.csv 可以使用上面的方法downloadFileToTmp下载文件到本地环境中
|
|
69
100
|
await hg.utils.file.csvRead(file_path,callback)
|
|
70
101
|
示例1 读取完成后返回数组
|
|
71
102
|
const list = await hg.utils.file.csvRead(file_path)
|
|
72
|
-
示例2
|
|
103
|
+
示例2 读取完成后回调, 超过5000条建议使用此方法防止内存溢出
|
|
73
104
|
await hg.utils.file.csvRead(file_path,async (row)=>{
|
|
74
105
|
// do something
|
|
75
106
|
})
|
|
107
|
+
|
|
108
|
+
3)解析excel文件,file_path必须是本地环境的文件路径 /tep/aaa.xlsx 可以使用上面的方法downloadFileToTmp下载文件到本地环境中
|
|
109
|
+
const list = hg.utils.file.xlsxReaderAll(file_path)
|
|
110
|
+
list是excel所有的内容,建议小文件使用(10M以下),如果是大文件10M以上转换成csv使用上面的方法,亲自测试10万条csv读取正常
|
|
76
111
|
```
|
|
77
112
|
|
|
78
113
|
## 小工具/多语言
|
|
@@ -84,8 +119,7 @@ await hg.utils.file.csvRead(file_path,async (row)=>{
|
|
|
84
119
|
hg.toMultilingual([{ lang: 'en-US', value: 'Regular' },{ lang: 'zh-CN', value: '正式' }]) 常用于开放平台返回的多语言数据转换
|
|
85
120
|
hg.toMultilingual(zh,en)
|
|
86
121
|
|
|
87
|
-
|
|
88
|
-
hg.toSafeValue(obj={},key,defValue)
|
|
122
|
+
|
|
89
123
|
|
|
90
124
|
```
|
|
91
125
|
|
package/utils/date_.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Date_.d.ts
|
|
2
|
+
export = Date_;
|
|
3
|
+
|
|
4
|
+
declare class Date_ {
|
|
5
|
+
constructor(hg: any);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 把日期字符串解析成时间戳(毫秒)
|
|
9
|
+
*/
|
|
10
|
+
dateStringToTimestamp(dateString: string): number;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 返回 moment-timezone 实例(已默认 Asia/Shanghai)
|
|
14
|
+
*/
|
|
15
|
+
moment(date?: string | number | Date): import('moment-timezone').Moment;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 当前时间按指定格式输出(默认 YYYY-MM-DD HH:mm:ss)
|
|
19
|
+
*/
|
|
20
|
+
nowFormat(format?: string): string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 把起止日期按指定月份跨度拆分成若干段
|
|
24
|
+
*/
|
|
25
|
+
splitDateRange(
|
|
26
|
+
startDate: string,
|
|
27
|
+
endDate: string,
|
|
28
|
+
splitMonths: number
|
|
29
|
+
): Array<{
|
|
30
|
+
begin: string; // yyyy-mm-dd
|
|
31
|
+
end: string; // yyyy-mm-dd
|
|
32
|
+
len: number; // 实际月份数
|
|
33
|
+
days: number; // 实际天数
|
|
34
|
+
}>;
|
|
35
|
+
}
|
package/utils/date_.js
CHANGED
|
@@ -4,10 +4,17 @@ class Date_{
|
|
|
4
4
|
constructor(hg){
|
|
5
5
|
this.#hg = hg
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
dateStringToTimestamp(dateString){
|
|
9
|
+
const timestamp = Date.parse(dateString);
|
|
10
|
+
//this.#hg.log4(`dateString=${dateString}, timestamp=${timestamp}`)
|
|
11
|
+
return timestamp
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
moment(date){
|
|
8
15
|
const moment = require('moment-timezone');
|
|
9
16
|
moment.tz.setDefault("Asia/Shanghai");
|
|
10
|
-
return moment()
|
|
17
|
+
return moment(date || Date.now())
|
|
11
18
|
}
|
|
12
19
|
nowFormat(format) {
|
|
13
20
|
const moment = require('moment-timezone');
|
package/utils/file_.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// File_.d.ts
|
|
2
|
+
export = File_;
|
|
3
|
+
|
|
4
|
+
declare class File_ {
|
|
5
|
+
constructor(hg: any);
|
|
6
|
+
|
|
7
|
+
/** 生成当前时间文件夹名:yyyy-MM-dd_HH-mm-ss */
|
|
8
|
+
getCurrentTimeFolderName(): string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 网络下载文件并上传到租户空间
|
|
12
|
+
* @param url 网络文件地址
|
|
13
|
+
* @param header 可选请求头
|
|
14
|
+
* @returns 上传后的文件信息
|
|
15
|
+
*/
|
|
16
|
+
downloadFileToUpload(
|
|
17
|
+
url: string,
|
|
18
|
+
header?: Record<string, string>
|
|
19
|
+
): Promise<FileInfo>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 下载租户空间的文件到环境 tmp 目录
|
|
23
|
+
* @param file_info 文件元数据
|
|
24
|
+
* @param file_path 本地存储路径(可选)
|
|
25
|
+
* @returns 本地文件路径
|
|
26
|
+
*/
|
|
27
|
+
downloadFileToTmp(
|
|
28
|
+
file_info: FileInfo,
|
|
29
|
+
file_path?: string
|
|
30
|
+
): Promise<string>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 使用 token 下载文件到环境 tmp 目录
|
|
34
|
+
* @param file_info 必须包含 token 与 mime_type
|
|
35
|
+
* @param file_path 本地存储路径(可选)
|
|
36
|
+
* @returns 本地文件路径
|
|
37
|
+
*/
|
|
38
|
+
downloadFileByToeknToTmp(
|
|
39
|
+
file_info: Pick<FileInfo, 'token' | 'mime_type'>,
|
|
40
|
+
file_path?: string
|
|
41
|
+
): Promise<string>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 将数据追加写入文件
|
|
45
|
+
* @param data 写入内容
|
|
46
|
+
* @param fileName 文件名(可选)
|
|
47
|
+
*/
|
|
48
|
+
saveDataToEnv(data: string, fileName?: string): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 读取 CSV 文件(依赖 csv-parser)
|
|
52
|
+
* @param path 文件路径
|
|
53
|
+
* @param callback 每行回调,返回 void 时一次性返回所有行
|
|
54
|
+
* @returns 无回调时返回全部行
|
|
55
|
+
*/
|
|
56
|
+
csvRead<T = Record<string, any>>(
|
|
57
|
+
path: string,
|
|
58
|
+
callback: (row: T, index: number) => void
|
|
59
|
+
): Promise<void>;
|
|
60
|
+
csvRead<T = Record<string, any>>(path: string): Promise<T[]>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 读取 Excel 文件(依赖 xlsx@0.18.5)
|
|
64
|
+
* @param filePath 文件路径
|
|
65
|
+
* @returns 工作表第一页的全部数据
|
|
66
|
+
*/
|
|
67
|
+
xlsxReaderAll(filePath: string): any[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* ── 依赖类型 ─────────────────────────────── */
|
|
71
|
+
interface FileInfo {
|
|
72
|
+
id?: string;
|
|
73
|
+
token?: string;
|
|
74
|
+
mime_type: string;
|
|
75
|
+
name?: string;
|
|
76
|
+
[key: string]: any;
|
|
77
|
+
}
|
package/utils/file_.js
CHANGED
|
@@ -41,6 +41,13 @@ class File_{
|
|
|
41
41
|
this.#hg.log4(`download success, path=` + _file_path)
|
|
42
42
|
return _file_path
|
|
43
43
|
}
|
|
44
|
+
async downloadFileByToeknToTmp(file_info, file_path){
|
|
45
|
+
this.#hg.log4(`download input=${ JSON.stringify(file_info)}`)
|
|
46
|
+
const _file_path = file_path || `/tmp/${Date.now()}.${ file_info.mime_type }`
|
|
47
|
+
await application.resources.file.download(file_info.token, _file_path);
|
|
48
|
+
this.#hg.log4(`download success, path=` + _file_path)
|
|
49
|
+
return _file_path
|
|
50
|
+
}
|
|
44
51
|
/**
|
|
45
52
|
* 写入文件
|
|
46
53
|
* @param {*} data 写入的内容
|
|
@@ -50,7 +57,7 @@ class File_{
|
|
|
50
57
|
fs.writeFileSync(fileName || `${ Date.now() }.txt`,data, { flag: 'a' })
|
|
51
58
|
}
|
|
52
59
|
/**
|
|
53
|
-
* 读取
|
|
60
|
+
* 读取csv文件(需要安装依赖:csv-parser)
|
|
54
61
|
* @param {*} path
|
|
55
62
|
* @param {*} callback(row) 每一行的回调
|
|
56
63
|
* @returns callback为null时,返回全部读取的结果list
|
|
@@ -59,10 +66,11 @@ class File_{
|
|
|
59
66
|
const csv = require('csv-parser');
|
|
60
67
|
return await new Promise((resolve, reject) => {
|
|
61
68
|
const list = []
|
|
69
|
+
let index = 0
|
|
62
70
|
fs.createReadStream(path).pipe(csv())
|
|
63
71
|
.on('data', (row) => {
|
|
64
72
|
if(callback){
|
|
65
|
-
callback(row)
|
|
73
|
+
callback(row,index++)
|
|
66
74
|
}else{
|
|
67
75
|
list.push(row)
|
|
68
76
|
}
|
|
@@ -76,14 +84,26 @@ class File_{
|
|
|
76
84
|
})
|
|
77
85
|
}
|
|
78
86
|
|
|
79
|
-
|
|
87
|
+
/**
|
|
88
|
+
* 读取Excel文件(需要安装依赖:xlsx@0.18.5)
|
|
89
|
+
* @param {*} filePath
|
|
90
|
+
* @returns 返回文件内容
|
|
91
|
+
*/
|
|
92
|
+
xlsxReaderAll(filePath){
|
|
80
93
|
const XLSX = require('xlsx');
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
let data = []
|
|
95
|
+
try {
|
|
96
|
+
const buffer = fs.readFileSync(filePath)
|
|
97
|
+
const workbook = XLSX.read(buffer, { type: 'buffer' });
|
|
98
|
+
const sheetName = workbook.SheetNames[0];
|
|
99
|
+
this.#hg.log4('Sheet Name:', sheetName);
|
|
100
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
101
|
+
data = XLSX.utils.sheet_to_json(worksheet);
|
|
102
|
+
this.#hg.log4('Data:', data);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
this.#hg.error('xlsxReaderAll - Error parsing Excel file:', error);
|
|
105
|
+
}
|
|
106
|
+
return data
|
|
87
107
|
}
|
|
88
108
|
}
|
|
89
109
|
module.exports = File_
|
package/utils/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
// Utils.d.ts
|
|
3
|
+
export = Utils;
|
|
4
|
+
|
|
5
|
+
declare class Utils {
|
|
6
|
+
constructor(hg: any);
|
|
7
|
+
|
|
8
|
+
/** 网络/文件/日期 子模块 */
|
|
9
|
+
readonly url: import('./url');
|
|
10
|
+
readonly file: import('./file_');
|
|
11
|
+
readonly date: import('./date_');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 数组分块(支持同步/异步回调)
|
|
15
|
+
* @param list 原始数组
|
|
16
|
+
* @param chunkSize 每块长度
|
|
17
|
+
* @param callback 每块回调;如提供,则整体返回 void
|
|
18
|
+
* @returns 无回调时返回分块后的二维数组
|
|
19
|
+
*/
|
|
20
|
+
splitArray<T = any>(
|
|
21
|
+
list: T[],
|
|
22
|
+
chunkSize: number,
|
|
23
|
+
callback?: (batch: T[]) => Promise<void> | void
|
|
24
|
+
): Promise<T[][] | void>;
|
|
25
|
+
|
|
26
|
+
/** 计算字符串 MD5 */
|
|
27
|
+
toMD5(data: string): string;
|
|
28
|
+
|
|
29
|
+
/** 按区域与币种格式化金额 */
|
|
30
|
+
formatCurrency(
|
|
31
|
+
amount: number,
|
|
32
|
+
locale?: string,
|
|
33
|
+
currency?: string
|
|
34
|
+
): string;
|
|
35
|
+
|
|
36
|
+
/** 将数组按固定大小分块(同步) */
|
|
37
|
+
chunkAll<T = any>(arr: T[], size: number): T[][];
|
|
38
|
+
|
|
39
|
+
/** 生成指定步长的数值范围数组 */
|
|
40
|
+
range(start: number, end: number, step?: number): number[];
|
|
41
|
+
|
|
42
|
+
/** 数组去重 */
|
|
43
|
+
unique<T = any>(arr: T[]): T[];
|
|
44
|
+
}
|
package/utils/index.js
CHANGED
|
@@ -42,5 +42,48 @@ class Utils{
|
|
|
42
42
|
return md5Hash
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 格式化货币
|
|
48
|
+
* @param {*} amount
|
|
49
|
+
* @param {*} locale
|
|
50
|
+
* @param {*} currency
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
formatCurrency(amount, locale = 'en-US', currency = 'USD') {
|
|
54
|
+
return new Intl.NumberFormat(locale, { style: 'currency', currency, }).format(amount);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 分块数组
|
|
59
|
+
* @param {*} arr
|
|
60
|
+
* @param {*} size
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
chunkAll(arr, size) {
|
|
64
|
+
return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, i * size + size));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 范围生成器
|
|
70
|
+
* @param {*} start
|
|
71
|
+
* @param {*} end
|
|
72
|
+
* @param {*} step
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
range(start, end, step = 1) {
|
|
76
|
+
return Array.from({ length: (end - start) / step + 1 }, (_, i) => start + i * step);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 移除重复项
|
|
81
|
+
* @param {*} arr
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
unique(arr) {
|
|
85
|
+
return [...new Set(arr)];
|
|
86
|
+
}
|
|
87
|
+
|
|
45
88
|
}
|
|
46
89
|
module.exports = Utils
|