bdip-office-assistant-cli 0.0.0-stage → 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 +37 -2
- package/dist/accounts.js +30 -0
- package/dist/args.js +36 -0
- package/dist/auth-token.js +783 -0
- package/dist/auth.js +331 -0
- package/dist/blog.js +562 -0
- package/dist/browser-links.js +224 -0
- package/dist/captcha-display.js +30 -0
- package/dist/cli.js +5533 -0
- package/dist/client.js +254 -0
- package/dist/config.js +170 -0
- package/dist/createcontext.js +41 -0
- package/dist/crypto.js +139 -0
- package/dist/cube.js +1771 -0
- package/dist/documents.js +98 -0
- package/dist/email.js +841 -0
- package/dist/field-preparation.js +237 -0
- package/dist/fileutil.js +20 -0
- package/dist/formula-runtime.js +55 -0
- package/dist/hrm.js +478 -0
- package/dist/keepalive.js +99 -0
- package/dist/kq.js +280 -0
- package/dist/linkage-calculations.js +68 -0
- package/dist/links.js +34 -0
- package/dist/meetings.js +172 -0
- package/dist/nodemap.js +242 -0
- package/dist/odoc.js +199 -0
- package/dist/office-tools.js +155 -0
- package/dist/pageopen.js +29 -0
- package/dist/parse.js +177 -0
- package/dist/password-input.js +84 -0
- package/dist/password-login.js +208 -0
- package/dist/person-selection.js +81 -0
- package/dist/querystate.js +38 -0
- package/dist/render.js +21 -0
- package/dist/report.js +50 -0
- package/dist/session-worker.js +54 -0
- package/dist/session.js +280 -0
- package/dist/submissionplan.js +45 -0
- package/dist/submissionstate.js +81 -0
- package/dist/subordinate.js +458 -0
- package/dist/supervise.js +152 -0
- package/dist/todoaskidea.js +54 -0
- package/dist/todobatch.js +172 -0
- package/dist/todocc.js +128 -0
- package/dist/todoforward.js +66 -0
- package/dist/todoreject.js +215 -0
- package/dist/todosubmit.js +325 -0
- package/dist/todotransfer.js +54 -0
- package/dist/util.js +25 -0
- package/dist/wfagent.js +538 -0
- package/dist/wfcreate.js +102 -0
- package/dist/wfform.js +169 -0
- package/dist/wfops.js +40 -0
- package/dist/wfreceiver.js +43 -0
- package/dist/wfstart.js +558 -0
- package/dist/wfsummary.js +89 -0
- package/dist/wfupload.js +83 -0
- package/dist/wfuser.js +96 -0
- package/dist/workflow-runtime.js +177 -0
- package/dist/workflow.js +570 -0
- package/dist/workplan.js +431 -0
- package/package.json +29 -6
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {resolveBrowserLinks} from './browser-links.js';
|
|
4
|
+
|
|
5
|
+
export function directories(tree) {
|
|
6
|
+
const rows=[];
|
|
7
|
+
const walk=(value,parent='')=>{
|
|
8
|
+
if(Array.isArray(value)){for(const v of value)walk(v,parent);return;}
|
|
9
|
+
if(!value||typeof value!=='object')return;
|
|
10
|
+
const full=value.categoryid!=null&&value.name?[parent,value.name].filter(Boolean).join('/'):parent;
|
|
11
|
+
if(value.categoryid!=null&&value.name)rows.push({id:String(value.categoryid),name:value.name,path:full,hasRight:value.hasRight});
|
|
12
|
+
for(const [key,v]of Object.entries(value))if(v&&typeof v==='object')walk(v,full);
|
|
13
|
+
};
|
|
14
|
+
walk(tree);return rows;
|
|
15
|
+
}
|
|
16
|
+
export async function request(api,method,route,body) {
|
|
17
|
+
const multipart=body instanceof FormData;
|
|
18
|
+
const headers=await api.getHeaders(method==='POST'&&!multipart?'application/x-www-form-urlencoded; charset=utf-8':undefined,{forceCookie:true});
|
|
19
|
+
const response=await fetch(new URL(route,api.baseUrl),{method,headers,body:method==='GET'?undefined:multipart?body:new URLSearchParams(body),signal:AbortSignal.timeout(120000)});
|
|
20
|
+
if(!response.ok)throw Error('文档接口HTTP '+response.status);
|
|
21
|
+
const json=await response.json();
|
|
22
|
+
if(json.api_status===false||json.status===false)throw Error('文档接口失败');
|
|
23
|
+
return json;
|
|
24
|
+
}
|
|
25
|
+
const plain=s=>String(s??'').replace(/<[^>]*>/g,'');
|
|
26
|
+
export async function listDocuments(api,id) {
|
|
27
|
+
const init=await request(api,'GET','/api/doc/searchlist/list?'+new URLSearchParams({seccategory:id,secid:id}));
|
|
28
|
+
if(String(init.seccategoryid)!==String(id)||!init.sessionkey)throw Error('文档列表目录范围未验证');
|
|
29
|
+
const items=[],seen=new Set();
|
|
30
|
+
for(let page=1;page<=1000;page++){
|
|
31
|
+
const result=await request(api,'POST','/api/ec/dev/table/datas',{dataKey:init.sessionkey,current:String(page),pageSize:'50'});
|
|
32
|
+
if(!Array.isArray(result.datas))throw Error('文档列表响应缺失');
|
|
33
|
+
for(const row of result.datas){
|
|
34
|
+
if(String(row.seccategory)!==String(id)||!row.id||seen.has(String(row.id)))throw Error('文档分页重复或范围不符');
|
|
35
|
+
seen.add(String(row.id));
|
|
36
|
+
const browser=result.browser?.find(b=>String(b.linkvaluecolumn)===String(row.id));
|
|
37
|
+
const link=String(row.idspan??'').match(/(\/spa\/document\/[^"'<>\s]+)/)?.[1];
|
|
38
|
+
items.push({id:String(row.id),title:plain(row.docsubject??row.idspan),directoryId:String(row.seccategory),ownerId:String(row.ownerid),creatorId:String(row.doccreaterid),attachmentCount:Number(row.accessorycount),status:plain(browser?.list?.find(v=>v.t_text==='状态')?.t_column)||null,url:link?new URL(link,api.baseUrl).href:null});
|
|
39
|
+
}
|
|
40
|
+
// 服务端可能缩小pageSize;只在空页终止,不能把不足请求大小当作末页。
|
|
41
|
+
if(result.datas.length===0)return items;
|
|
42
|
+
}
|
|
43
|
+
throw Error('文档分页超过上限,不能声明完整');
|
|
44
|
+
}
|
|
45
|
+
export async function withDocumentLinks(cfg, rows) {
|
|
46
|
+
const links=await resolveBrowserLinks(cfg,rows.map(row=>row.url));
|
|
47
|
+
return rows.map((row,index)=>{
|
|
48
|
+
const link=links[index];
|
|
49
|
+
return {...row,...link};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function uploadDefaults(init,file) {
|
|
53
|
+
if(init.canEdit!==true)throw Error('目录不允许上传');
|
|
54
|
+
const size=fs.statSync(file).size;
|
|
55
|
+
const max=Number(init.maxUploadFileSize),min=Number(init.mixUploadFileSize);
|
|
56
|
+
if(!Number.isFinite(max)||!Number.isFinite(min))throw Error('目录大小规则未知');
|
|
57
|
+
if((max>0&&size>max*1024)||size<min*1024)throw Error('文件大小不符合目录限制');
|
|
58
|
+
const ext=path.extname(file).slice(1).toLowerCase(),allowed=String(init.uploadext??'').toLowerCase();
|
|
59
|
+
if(!['*.*','*'].includes(allowed)&&!allowed.split(/[;,\s]+/).map(x=>x.replace(/^\*?\./,'')).includes(ext))throw Error('扩展名不允许或规则未识别');
|
|
60
|
+
const defaults={};
|
|
61
|
+
const walk=values=>{for(const c of values??[]){if(c.items){walk(c.items);continue;}const key=c.domkey?.[0];if(!key)throw Error('未知文档初始化字段');const value=c.otherParams?.value??c.browserConditionParam?.replaceDatas?.map(x=>x.id).join(',')??c.value;if(value!=null&&value!=='')defaults[key]=String(value);else if(Number(c.viewAttr)===3)throw Error('缺少必填文档属性:'+key);}};
|
|
62
|
+
walk(init.conditions);
|
|
63
|
+
if(!defaults.ownerid)throw Error('未取得默认所有者');
|
|
64
|
+
return defaults;
|
|
65
|
+
}
|
|
66
|
+
export async function documentMain(api,args,accountId,cfg) {
|
|
67
|
+
const [action,id,file,receipt]=args;
|
|
68
|
+
if(action==='directories')return directories((await request(api,'GET','/api/doc/console/category/tree')).treeDatas).filter(r=>!id||r.name.includes(id)||r.path.includes(id));
|
|
69
|
+
if(!/^\d+$/.test(id??''))throw Error('请先按名查询并选择目录ID');
|
|
70
|
+
if(action==='list')return withDocumentLinks(cfg,await listDocuments(api,id));
|
|
71
|
+
if(action==='init')return request(api,'GET','/api/doc/console/multi/init?'+new URLSearchParams({secCategoryId:id}));
|
|
72
|
+
if(action==='upload'){
|
|
73
|
+
if(!file||!receipt||!args.includes('--confirm'))throw Error('上传需要文件、全新回执路径和--confirm;仅保存,不发布');
|
|
74
|
+
const init=await request(api,'GET','/api/doc/console/multi/init?'+new URLSearchParams({secCategoryId:id}));
|
|
75
|
+
const defaults=uploadDefaults(init,file);
|
|
76
|
+
const before=await listDocuments(api,id),beforeIds=new Set(before.map(r=>r.id));
|
|
77
|
+
const state={state:'prepared',directoryId:id,accountId,file:path.resolve(file),ownerId:defaults.ownerid};
|
|
78
|
+
fs.writeFileSync(receipt,JSON.stringify(state,null,2),{flag:'wx'});
|
|
79
|
+
const saveState=()=>fs.writeFileSync(receipt,JSON.stringify(state,null,2));
|
|
80
|
+
try{
|
|
81
|
+
state.state='uploading';saveState();
|
|
82
|
+
const form=new FormData();form.append('category','document');form.append('file',new Blob([fs.readFileSync(file)]),path.basename(file));
|
|
83
|
+
const up=await request(api,'POST','/api/doc/upload/uploadFile',form);
|
|
84
|
+
if(String(up.status)!=='1'||!up.data?.fileid)throw Error('附件上传未确认');
|
|
85
|
+
state.attachmentId=String(up.data.fileid);state.state='saving';saveState();
|
|
86
|
+
const identity=await request(api,'GET','/api/hrm/login/getAccountList');
|
|
87
|
+
const current=identity.data?.accountlist?.filter(a=>String(a.iscurrent)==='1');
|
|
88
|
+
if(current?.length!==1||String(current[0].userid)!==accountId)throw Error('上传期间账号变化,停止保存');
|
|
89
|
+
const saved=await request(api,'POST','/api/doc/console/multi/saveUpload',{...defaults,secCategoryId:id,seccategory:id,fileIds:state.attachmentId});
|
|
90
|
+
if(saved.api_status!==true)throw Error('文档保存未确认');
|
|
91
|
+
const title=path.parse(up.data.filename||path.basename(file)).name;
|
|
92
|
+
const matches=(await listDocuments(api,id)).filter(r=>!beforeIds.has(r.id)&&r.title===title&&r.ownerId===defaults.ownerid&&r.creatorId===accountId&&r.attachmentCount===1&&r.status==='草稿');
|
|
93
|
+
if(matches.length!==1)throw Error('保存后文档回查不唯一或属性不一致');
|
|
94
|
+
state.state='draft-verified';state.document=(await withDocumentLinks(cfg,matches))[0];saveState();return state;
|
|
95
|
+
}catch(error){state.state='uncertain';state.error=error.message;saveState();throw Error(error.message+';查阅回执并查询目录核验,禁止自动重复上传/保存');}
|
|
96
|
+
}
|
|
97
|
+
throw Error('文档命令:directories [名称] | list <目录ID> | init <目录ID> | upload <目录ID> <文件> <新回执路径> --confirm');
|
|
98
|
+
}
|