bdip-office-assistant-cli 1.0.0 → 1.0.1
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/README.md +4 -4
- package/dist/auth-token.js +92 -87
- package/dist/auth.js +45 -4
- package/dist/browser-links.js +1 -0
- package/dist/cli.js +58 -4
- package/dist/documents.js +98 -0
- package/dist/field-preparation.js +61 -9
- package/dist/formula-runtime.js +55 -0
- package/dist/linkage-calculations.js +68 -0
- package/dist/meetings.js +172 -0
- package/dist/office-tools.js +19 -2
- package/dist/person-selection.js +81 -0
- package/dist/wfupload.js +14 -9
- package/dist/workflow-runtime.js +29 -3
- package/package.json +4 -3
package/dist/wfupload.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
|
+
import { createApiFromProfile } from './client.js';
|
|
14
15
|
const IMG_EXT = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg', 'tif', 'tiff']);
|
|
15
16
|
/** 判断文件按图片还是附件上传(按扩展名;可被 --list-type 覆盖)。 */
|
|
16
17
|
export function guessListType(filePath) {
|
|
@@ -25,7 +26,10 @@ export async function uploadFile(cfg, opts) {
|
|
|
25
26
|
const { workflowId, fieldId, filePath } = opts;
|
|
26
27
|
const listType = opts.listType ?? guessListType(filePath);
|
|
27
28
|
const category = opts.category ?? '21';
|
|
28
|
-
const loginuserid = opts.loginuserid
|
|
29
|
+
const loginuserid = opts.loginuserid;
|
|
30
|
+
if (!/^[1-9]\d*$/.test(String(loginuserid ?? '')) || !/^\d+$/.test(String(workflowId)) || !/^\d+$/.test(String(fieldId)))
|
|
31
|
+
throw new Error('上传需要已核实的当前账号、流程和字段 ID');
|
|
32
|
+
if (!['img', 'list'].includes(listType)) throw new Error('不支持的附件类型');
|
|
29
33
|
if (!fs.existsSync(filePath))
|
|
30
34
|
throw new Error(`文件不存在: ${filePath}`);
|
|
31
35
|
const buf = fs.readFileSync(filePath);
|
|
@@ -35,9 +39,10 @@ export async function uploadFile(cfg, opts) {
|
|
|
35
39
|
: ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg'
|
|
36
40
|
: ext === 'gif' ? 'image/gif'
|
|
37
41
|
: 'application/octet-stream';
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
42
|
+
const api = opts.api ?? await createApiFromProfile(cfg);
|
|
43
|
+
const headers = await api.getHeaders();
|
|
44
|
+
delete headers['Content-Type'];
|
|
45
|
+
const base = api.baseUrl.replace(/\/+$/, '');
|
|
41
46
|
const url = `${base}/api/doc/upload/uploadFile2Doc?category=,,${category}&workflowid=${workflowId}&listType=${listType}&wfTestStr=&uploadSrc=wfform&loginuserid=${loginuserid}`;
|
|
42
47
|
const fd = new FormData();
|
|
43
48
|
fd.append('name', filename);
|
|
@@ -49,18 +54,18 @@ export async function uploadFile(cfg, opts) {
|
|
|
49
54
|
fd.append('file', new Blob([buf], { type: mime }), filename);
|
|
50
55
|
const r = await fetch(url, {
|
|
51
56
|
method: 'POST',
|
|
52
|
-
headers
|
|
57
|
+
headers,
|
|
53
58
|
body: fd,
|
|
54
59
|
signal: AbortSignal.timeout(60000),
|
|
60
|
+
redirect: 'manual',
|
|
55
61
|
});
|
|
56
|
-
const t = await r.text();
|
|
57
62
|
let j = null;
|
|
58
63
|
try {
|
|
59
|
-
j =
|
|
64
|
+
j = await r.json();
|
|
60
65
|
}
|
|
61
66
|
catch { /* non-json */ }
|
|
62
|
-
if (!r.ok ||
|
|
63
|
-
throw new Error(
|
|
67
|
+
if (!r.ok || j?.status === false || j?.api_status === false || !/^\d+$/.test(String(j?.data?.fileid ?? ''))) {
|
|
68
|
+
throw new Error(`附件上传结果未确认,HTTP ${r.status};请检查上传回执,不要立即重复上传`);
|
|
64
69
|
}
|
|
65
70
|
const d = j.data;
|
|
66
71
|
return {
|
package/dist/workflow-runtime.js
CHANGED
|
@@ -5,6 +5,7 @@ import {pathToFileURL} from 'node:url';
|
|
|
5
5
|
import {createApiFromProfile} from './client.js';
|
|
6
6
|
import {buildCreateSubmitBody} from './wfstart.js';
|
|
7
7
|
import {stableConfig} from './field-preparation.js';
|
|
8
|
+
import {queryPersonBrowser} from './person-selection.js';
|
|
8
9
|
|
|
9
10
|
const idOf = id => String(id).replace(/^field/, '');
|
|
10
11
|
export function definitions(form) {
|
|
@@ -12,7 +13,7 @@ export function definitions(form) {
|
|
|
12
13
|
}
|
|
13
14
|
export function fingerprint(form) {
|
|
14
15
|
const schema = definitions(form).map(f => [f.table,f.id,f.fieldname,f.fieldlabel,f.viewattr,f.htmltype,f.detailtype,f.fielddbtype,f.browserattr]).sort((a,b)=>String(a).localeCompare(String(b)));
|
|
15
|
-
const browsers =
|
|
16
|
+
const browsers = stableConfig(form.browserInfo ?? {});
|
|
16
17
|
return createHash('sha256').update(JSON.stringify(['unified-linkage-2',schema,browsers,form.params?.formid,form.params?.layoutversion,stableConfig(form.linkageCfg??{})])).digest('hex');
|
|
17
18
|
}
|
|
18
19
|
export function adapterDirectory(cfg, workflowId) {
|
|
@@ -52,13 +53,36 @@ export async function detailTables(api,form,workflowId,requestId) {
|
|
|
52
53
|
}
|
|
53
54
|
export function browserQuery(form,fieldId,context={}) {
|
|
54
55
|
const f=definitions(form).find(x=>x.id===String(fieldId));if(!f)throw Error('浏览框字段不存在');
|
|
55
|
-
const
|
|
56
|
+
const source=form.browserInfo?.[`${f.detailtype}_${f.fielddbtype}`]??form.browserInfo?.[String(f.detailtype)];
|
|
57
|
+
const b=source?{...source,...source.otherParams}:null;
|
|
56
58
|
if(!b)throw Error('缺少浏览框配置,请读取网页配置后适配');
|
|
57
59
|
const p=form.params;
|
|
58
60
|
const query={...b.conditionDataParams,...b.completeParams,...b.dataParams,wfid:String(p.workflowid),billid:p.formid,isbill:p.isbill,
|
|
59
61
|
requestid:String(p.requestid??'-1'),fieldid:String(fieldId),viewtype:f.table==='main'?'0':'1',fromModule:'workflow',
|
|
60
62
|
f_weaver_belongto_userid:p.f_weaver_belongto_userid,f_weaver_belongto_usertype:p.f_weaver_belongto_usertype,
|
|
63
|
+
wf_isagent:p.isagent,wf_beagenter:p.beagenter,wfTestStr:p.wfTestStr,wfCreater:p.creater,disabledConditionCache:true,
|
|
61
64
|
current:'1',pageSize:String(context.pageSize??10),min:'1',max:String(context.pageSize??10)};
|
|
65
|
+
Object.assign(query,f.browserattr?.browserParams??{});
|
|
66
|
+
const cellValue=id=>{
|
|
67
|
+
const dependency=definitions(form).find(x=>x.id===String(id));
|
|
68
|
+
if(!dependency)throw Error('依赖字段不存在');
|
|
69
|
+
const values=dependency.table==='main'?context.main:context.row;
|
|
70
|
+
const cell=dependency.table==='main'?form.maindata?.['field'+id]:context.rowCells?.['field'+id];
|
|
71
|
+
const value=values?.[id]??values?.['field'+id]??cell?.value;
|
|
72
|
+
if(value===undefined)throw Error('缺少同一行/主表依赖值:'+id);
|
|
73
|
+
return value;
|
|
74
|
+
};
|
|
75
|
+
for(const ref of b.relateFieldid??[]) {
|
|
76
|
+
const [id,isDetail]=String(ref).split('_'),dep=definitions(form).find(x=>x.id===id);
|
|
77
|
+
if(!dep?.fieldname)throw Error('关联字段名称缺失');
|
|
78
|
+
let key=isDetail==='1'?dep.tableName+'_'+dep.fieldname:dep.fieldname;
|
|
79
|
+
if([161,162,256,257].includes(Number(f.detailtype)))key=key.toLowerCase()+'_'+b.dataParams?.currenttime;
|
|
80
|
+
query[key]=cellValue(id);
|
|
81
|
+
}
|
|
82
|
+
for(const ref of f.browserattr?.relateFieldid??[]) {
|
|
83
|
+
const id=String(ref).split('_')[0];query['field'+id]=cellValue(id);
|
|
84
|
+
}
|
|
85
|
+
if(context.customDataParam||context.customBrowserProp)throw Error('自定义浏览行为须经本地适配验证');
|
|
62
86
|
for(const dep of f.browserattr?.cusDependShip??[]) {
|
|
63
87
|
const dependency=definitions(form).find(x=>x.id===String(dep.fieldid));
|
|
64
88
|
if(!dependency)throw Error('依赖字段不存在');
|
|
@@ -69,10 +93,12 @@ export function browserQuery(form,fieldId,context={}) {
|
|
|
69
93
|
if(!dep.key||!b.dataParams?.currenttime)throw Error('缺少依赖字段key或服务器时间标识');
|
|
70
94
|
query[dep.key.toLowerCase()+'_'+b.dataParams.currenttime]=value;
|
|
71
95
|
}
|
|
72
|
-
|
|
96
|
+
// undefined 参数不能编码成字符串 undefined。
|
|
97
|
+
return {field:f,query:Object.fromEntries(Object.entries(query).filter(([,v])=>v!==undefined))};
|
|
73
98
|
}
|
|
74
99
|
export async function queryBrowser(api,form,fieldId,context={}) {
|
|
75
100
|
const {field,query}=browserQuery(form,fieldId,context);
|
|
101
|
+
if(Number(field.htmltype)===3&&[1,17].includes(Number(field.detailtype)))return queryPersonBrowser(api,form,field,query,context);
|
|
76
102
|
const condition=await api.get('/api/public/browser/condition/'+field.detailtype,query,{forceCookie:true});
|
|
77
103
|
const conditions=condition.json?.conditions??condition.json?.conditioninfo?.conditions;
|
|
78
104
|
if(context.search) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdip-office-assistant-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "毕埃慕ai助手 CLI:待办、流程、台账、日志、邮件、日程、公文、考勤、会议及文档",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bdip": "dist/cli.js"
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"bdip": "node dist/cli.js"
|
|
21
21
|
},
|
|
22
22
|
"author": "毕埃慕(上海)建筑数据技术股份有限公司",
|
|
23
|
-
"releaseVersion": "1.0.
|
|
23
|
+
"releaseVersion": "1.0.1",
|
|
24
|
+
"desktopLoginContract": "verified-auto-close-v1",
|
|
24
25
|
"publishConfig": {
|
|
25
26
|
"registry": "https://registry.npmjs.org/",
|
|
26
27
|
"access": "public"
|