apass-opensdk-hugong 1.0.4 → 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.
@@ -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,id){
8
- await application.data.object(table).select('_id').where({_id: id ? id : application.operator.gte(0)}).findStream(async records=>{
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', list.length)
18
+ this.#hg.log4('find all len', records.length)
19
19
  await callback(records)
20
20
  }else{
21
- this.#hg.log4('find all size', list.length)
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
@@ -10,10 +10,6 @@ class HG{
10
10
  #app_secret
11
11
  constructor(logger){
12
12
  this.setLogger(logger)
13
- this._time = null
14
- }
15
- setLogger(logger){
16
- this.#logger = logger
17
13
  this.users = new Users(this)
18
14
  this.document = new Document(this)
19
15
  this.employee = new Employee(this)
@@ -21,6 +17,16 @@ class HG{
21
17
  this.object = new Object_(this)
22
18
 
23
19
  this.utils = new Utils(this)
20
+ this._time = null
21
+ }
22
+ async setLogger(logger){
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
- setAppId(app_id,app_secret){
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
@@ -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
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "apass-opensdk-hugong",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "飞书Apass低代码平台-飞书开放平台-相关的接口整合和常用的方法整合",
5
5
  "main": "index.js",
6
+ "types": "index.d.ts",
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1"
8
9
  },
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 list = hg.utils.splitArray([], 50) 输出 [ [50],[50] ]
46
-
47
- 第二种用法,每次输出50条 处理完成后继续下次执行
48
- await hg.utils.splitArray([], 10, async (items)=>{
49
- // do something
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, name, ...}
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
 
@@ -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
- moment(){
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');
@@ -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
- * 读取Excel文件(需要安装依赖:csv-parser)
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
- async excelReader(path,callback){
87
+ /**
88
+ * 读取Excel文件(需要安装依赖:xlsx@0.18.5)
89
+ * @param {*} filePath
90
+ * @returns 返回文件内容
91
+ */
92
+ xlsxReaderAll(filePath){
80
93
  const XLSX = require('xlsx');
81
- const stream = fs.createReadStream(path.join(__dirname, 'example.xlsx'));
82
- const workbook = XLSX.read(stream, { type: 'buffer' });
83
- const sheetName = workbook.SheetNames[0];
84
- const worksheet = workbook.Sheets[sheetName];
85
- const data = XLSX.utils.sheet_to_json(worksheet);
86
- console.log(data);
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_
@@ -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