neo-cmp-cli 1.13.10 → 1.13.12
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/dist/neo/neoEnvManager.js +1 -1
- package/dist/neo/neoLogin.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +1 -1
- package/template/antd-custom-cmp-template/package.json +1 -1
- package/template/asset-manage-template/package.json +1 -1
- package/template/echarts-custom-cmp-template/package.json +1 -1
- package/template/empty-custom-cmp-template/package.json +1 -1
- package/template/map-custom-cmp-template/package.json +1 -1
- package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +279 -0
- package/template/neo-bi-cmps/docs/gartner-pipeline-prd.md +389 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/SKILL.md +188 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/01-Trigger/345/274/200/345/217/221.md +183 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/02-/350/207/252/345/256/232/344/271/211API/345/274/200/345/217/221.md +196 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/03-SDK/345/267/245/345/205/267/347/261/273/346/216/245/345/217/243.md +346 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/04-/350/256/241/345/210/222/344/275/234/344/270/232/345/274/200/345/217/221.md +188 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/05-/351/241/265/351/235/242/345/274/200/345/217/221.md +293 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/06-/346/265/201/347/250/213/346/211/251/345/261/225/345/274/200/345/217/221.md +175 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/PaaS/345/271/263/345/217/260/345/274/200/345/217/221/346/211/213/345/206/214/350/247/243/350/257/273.md +313 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/auth-config.md +77 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/deploy_server_script.py +118 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/download_server_script.py +74 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entity_desc.py +69 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entitylist.py +87 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/query_crm.py +65 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/uninstall_server_script.py +48 -0
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/update_model_jar.py +49 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/SKILL.md +138 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/auth-config.md +77 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/component-dev.md +205 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/entityTable-example.md +167 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/templates.md +38 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entity_desc.py +69 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entitylist.py +87 -0
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/query_crm.py +65 -0
- package/template/neo-bi-cmps/docs/prototype-pipeline-forecasting.html +2453 -0
- package/template/neo-bi-cmps/docs//350/264/246/345/217/267/347/233/270/345/205/263/344/277/241/346/201/257.md +10 -0
- package/template/neo-bi-cmps/package.json +1 -1
- package/template/neo-custom-cmp-template/package.json +1 -1
- package/template/neo-custom-cmp-template/src/components/entityForm__c/index.tsx +48 -54
- package/template/neo-custom-cmp-template/src/components/entityForm__c/model.ts +1 -1
- package/template/neo-custom-cmp-template/src/components/entityForm__c/style.scss +80 -77
- package/template/neo-h5-cmps/package.json +1 -1
- package/template/neo-order-cmps/package.json +1 -1
- package/template/neo-web-entity-grid/package.json +1 -1
- package/template/neo-web-entity-grid/src/components/createForm__c/index.tsx +46 -54
- package/template/neo-web-entity-grid/src/components/createForm__c/resetAntd.scss +74 -0
- package/template/neo-web-entity-grid/src/components/createForm__c/style.scss +81 -152
- package/template/neo-web-entity-grid/src/components/searchForm__c/index.tsx +47 -52
- package/template/neo-web-entity-grid/src/components/searchForm__c/style.scss +60 -74
- package/template/neo-web-form/package.json +1 -1
- package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +16 -7
- package/template/neo-web-form/src/components/batchAddTable__c/style.scss +14 -0
- package/template/neo-web-form/src/components/batchAddTable__c/tableModal.scss +60 -13
- package/template/react-custom-cmp-template/package.json +1 -1
- package/template/react-ts-custom-cmp-template/package.json +1 -1
- package/template/vue2-custom-cmp-template/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("node:fs"),t=require("node:path"),n=require("../utils/common.js"),i=require("../config/index.js"),r=require("../config/auth.config.js"),s=require("./env.js");var o,u;exports.__require=function(){if(u)return o;u=1;const c=e,g=t,{errorLog:a}=n.__require(),f=i.__require(),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("node:fs"),t=require("node:path"),n=require("../utils/common.js"),i=require("../config/index.js"),r=require("../config/auth.config.js"),s=require("./env.js");var o,u;exports.__require=function(){if(u)return o;u=1;const c=e,g=t,{errorLog:a}=n.__require(),f=i.__require(),h=r.__require(),{DefaultNeoCrmAPI:l}=s.__require();return o=class{static getEnvFilePath(){return g.join(process.cwd(),".neo-cli","env.json")}static ensureEnvDir(){const e=g.dirname(this.getEnvFilePath());c.existsSync(e)||c.mkdirSync(e,{recursive:!0})}static getAuthConfig(){return{...l,...f.neoConfig,...h}}static getEnvConfig(){let e={};const t=this.getEnvFilePath(),n=this.getAuthConfig();if(!c.existsSync(t))return n;try{const n=c.readFileSync(t,"utf-8");e=JSON.parse(n)}catch(e){a(`读取环境配置文件失败: ${e.message||e.msg}`)}return{...n,...e}}static getLinKDebugURL(e){const t=this.getEnvConfig(),n=t.neoBaseURL;return"web"===e?`${n}${t.pcLinkDebugUrl}`:`${n}${t.h5LinkDebugUrl}`}static setEnvConfig(e){if(!e||"object"!=typeof e)throw new Error("环境配置必须是一个对象");this.ensureEnvDir();const t={...this.getEnvConfig(),...e,updatedAt:Date.now()},n=this.getEnvFilePath();return c.writeFileSync(n,JSON.stringify(t,null,2),"utf-8"),t}static updateEnvConfig(e){return this.setEnvConfig(e)}static clearEnvConfig(){const e=this.getEnvFilePath();c.existsSync(e)&&c.unlinkSync(e)}}};
|
package/dist/neo/neoLogin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("axios"),t=require("node:fs"),n=require("node:path"),s=require("ora"),r=require("node:http"),o=require("node:url"),i=require("open"),a=require("node:net"),c=require("lodash"),l=require("../utils/common.js"),h=require("./neoEnvManager.js");var p,u;exports.__require=function(){if(u)return p;u=1;const d=e,g=t,m=n,y=s,k=r,_=o,f=i,T=a,
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("axios"),t=require("node:fs"),n=require("node:path"),s=require("ora"),r=require("node:http"),o=require("node:url"),i=require("open"),a=require("node:net"),c=require("lodash"),l=require("../utils/common.js"),h=require("./neoEnvManager.js");var p,u;exports.__require=function(){if(u)return p;u=1;const d=e,g=t,m=n,y=s,k=r,_=o,f=i,T=a,w=c,{errorLog:U,successLog:R}=l.__require(),$=h.__require();return p=class{constructor(e){if(this.tokenDir=m.join(process.cwd(),".neo-cli"),this.tokenFile=m.join(this.tokenDir,"token.json"),this.pageDir=m.join(__dirname,"../../template/pageHtml"),this.authErrorTemplate=m.join(this.pageDir,"auth-error.html"),this.authSuccessTemplate=m.join(this.pageDir,"auth-success.html"),this.tokenErrorTemplate=m.join(this.pageDir,"token-error.html"),this.authFailedTemplate=m.join(this.pageDir,"auth-failed.html"),!e)return;const{neoBaseURL:t,loginURL:n,tokenURL:s}=e,r=$.getAuthConfig(),{redirectUri:o,authType:i,auth:a}=r;if(!n||!s)throw new Error("neo.config.js 配置不完整,需要包含 loginURL、tokenURL");this.neoBaseURL=t,this.loginURL=n,this.tokenURL=s,this.NeoCrmAPI=r,this.redirectUri=o,this.authType=i||"oauth2",this.response_type=a.response_type||"code",this.client_id=a.client_id,this.client_secret=a.client_secret,this.scope=a.scope||"all",this.oauthType=a.oauthType||"standard",this.access_type=a.access_type||"offline",this.grant_type=a.grant_type||"authorization_code";try{$.setEnvConfig(w.pick(e,["neoBaseURL","loginURL","tokenURL"]))}catch(e){U(`保存环境配置失败: ${e.message||e.msg}`)}}ensureTokenDir(){g.existsSync(this.tokenDir)||g.mkdirSync(this.tokenDir,{recursive:!0})}saveToken(e){this.ensureTokenDir();const t={...e,savedAt:Date.now(),expiresAt:Date.now()+1e3*(e.expires_in||7200)};g.writeFileSync(this.tokenFile,JSON.stringify(t,null,2),"utf-8")}readToken(){if(!g.existsSync(this.tokenFile))return null;try{return JSON.parse(g.readFileSync(this.tokenFile,"utf-8"))}catch(e){return U(`读取 token 文件失败: ${e.message||e.msg}`),null}}isTokenExpired(e){return!e||!e.expiresAt||Date.now()>=e.expiresAt-3e5}clearToken(){g.existsSync(this.tokenFile)&&g.unlinkSync(this.tokenFile)}getRedirectURI(e){return`http://localhost:${e}`}buildAuthUrl(e){const t=new URLSearchParams({response_type:this.response_type,client_id:this.client_id,redirect_uri:e,scope:this.scope,oauthType:this.oauthType,access_type:this.access_type});return`${this.loginURL}?${t.toString()}`}async openBrowser(e){try{await f(e)}catch(t){U(`无法自动打开浏览器: ${t.message||t.msg}`),console.log(`\n请手动访问以下 URL 进行授权:\n${e}\n`)}}getHtmlTemplate(e,t={}){try{let n=g.readFileSync(e,"utf-8");return Object.keys(t).forEach(e=>{const s=`{{${e}}}`;n=n.replace(new RegExp(s,"g"),t[e])}),n}catch(e){return U(`读取 HTML 模板失败: ${e.message||e.msg}`),"<html><body><h1>页面加载失败</h1></body></html>"}}async isPortInUse(e){return new Promise(t=>{const n=T.createServer();n.once("error",e=>{"EADDRINUSE"===e.code?t(!0):t(!1)}),n.once("listening",()=>{n.once("close",()=>{t(!1)}),n.close()}),n.listen(e)})}async startCallbackServer(){let e=this.redirectUri,t=new URL(e),n=parseInt(t.port,10);await this.isPortInUse(n)&&(U(`\n警告: 端口 ${n} 已被占用,请调整 redirectUri 配置项,使其指向一个未被占用的端口。`),process.exit(1));const s=new Promise((s,r)=>{const o=k.createServer(async(n,i)=>{const a=_.parse(n.url,!0);if(a.pathname===t.pathname||"/"===a.pathname){const t=a.query.code,n=a.query.error;if(n){i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const e=this.getHtmlTemplate(this.authErrorTemplate,{ERROR:n});return i.end(e),o.close(),void r(new Error(`授权失败: ${n}`))}if(t)try{const n=await this.getTokenByCode(t,e);this.saveToken(n);const r=n.userInfo.tenantName?`${n.userInfo.tenantName}(${n.tenant_id})`:n.tenant_id||"未返回",a=n.instance_uri||"未返回";i.writeHead(200,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authSuccessTemplate,{TENANT_ID:r,INSTANCE_URI:a});return i.end(c),o.close(),void s({code:t,tokenData:n})}catch(e){i.writeHead(500,{"Content-Type":"text/html; charset=utf-8"});const t=this.getHtmlTemplate(this.tokenErrorTemplate,{ERROR:e});return i.end(t),o.close(),void r(e)}i.writeHead(400,{"Content-Type":"text/html; charset=utf-8"});const c=this.getHtmlTemplate(this.authFailedTemplate);i.end(c),o.close(),r(new Error("未获取到授权码"))}else i.writeHead(404,{"Content-Type":"text/plain"}),i.end("Not Found")});o.on("error",e=>{"EADDRINUSE"===e.code?r(new Error(`端口 ${n} 已被占用,无法启动回调服务器`)):r(e)}),o.listen(n,()=>{console.log(`\n本地回调服务器已启动,监听端口: ${n}`),console.log(`回调地址: ${e}`)}),setTimeout(()=>{o.close(),r(new Error("授权超时,请重试"))},3e5)});return{redirectUri:e,getCodeAndTokenPromise:s}}async getTokenByCode(e,t){const n=y("正在获取 access token...").start();try{const s=new URLSearchParams;s.append("grant_type",this.grant_type),s.append("client_id",this.client_id),s.append("client_secret",this.client_secret),s.append("code",e),s.append("redirect_uri",t);const r=(await d.post(this.tokenURL,s.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;if(r&&r.access_token){r&&r.instance_uri&&r.instance_uri!==this.neoBaseURL&&(this.neoBaseURL=r.instance_uri,$.setEnvConfig({neoBaseURL:r.instance_uri}));const e=await this.getUserInfo(r.access_token);e&&e.tenantName&&e.name?r.userInfo=e:U("获取用户信息失败:",e)}else U("获取 token 失败:响应中未包含 access_token",n),U(`响应数据: ${JSON.stringify(r)}`),process.exit(1);return R("成功获取 access token",n),r}catch(e){U("获取 token 失败",n),U(`\n获取 token 失败: ${e.message||e.msg}`),e.response&&U(`响应数据: ${JSON.stringify(e.response.data)}`),process.exit(1)}}async refreshToken(e){const t=y("正在刷新授权信息(token)...").start();try{const n=new URLSearchParams;n.append("grant_type","refresh_token"),n.append("client_id",this.client_id),n.append("client_secret",this.client_secret),n.append("refresh_token",e);const s=(await d.post(this.tokenURL,n.toString(),{headers:{"Content-Type":"application/x-www-form-urlencoded"}})).data;if(!s||!s.access_token)return U("刷新授权信息失败:响应中未包含 access_token",t),U(`响应数据: ${JSON.stringify(s)}`),null;{const e=await this.getUserInfo(s.access_token);s.userInfo=e}return R("刷新授权信息成功(token)。",t),s}catch(e){return U("刷新授权信息失败",t),U(`\n刷新授权信息失败: ${e.message||e.msg}`),e.response&&U(`响应数据: ${JSON.stringify(e.response.data)}`),null}}async getUserInfo(e){let t={};try{let n=this.buildFullUrl(this.NeoCrmAPI.getUserInfoAPI);const s=await d.get(n,{headers:{Authorization:`Bearer ${e}`,"xsy-inner-source":"bff","Content-Type":"application/json"}}),{code:r,message:o,msg:i}=s.data||{};r&&"200"!==r&&(U(`获取用户信息失败: ${o||i||"未知错误"}`),process.exit(1)),t=s.data.result||s.data.data||{}}catch(e){const t=e.message||e.msg;U(t?`获取用户信息失败: ${t}`:`响应数据: ${JSON.stringify(e)}`),process.exit(1)}return t}async login(){console.log("\n========== NeoCRM 登录授权 ==========\n");try{const{redirectUri:e,getCodeAndTokenPromise:t}=await this.startCallbackServer(),n=this.buildAuthUrl(e);console.log("授权 URL:",n),console.log("\n正在打开浏览器进行授权..."),await this.openBrowser(n);const{code:s,tokenData:r}=await t;return R("\n✓ 已获取授权码"),console.log("\n========== 登录成功 ==========\n"),console.log(`实例地址: ${r.instance_uri||"未返回"}`),console.log(`当前租户信息: ${r.userInfo.tenantName||"未返回租户名称"}(${r.tenant_id||"未返回租户 ID"})`),console.log(`当前登录用户: ${r.userInfo.name||"未返回用户名"}`),r}catch(e){U(`\n登录失败: ${e.message||e.msg}`),process.exit(1)}}async logout(){if(console.log("\n========== NeoCRM 登出 ==========\n"),g.existsSync(this.tokenFile))try{$.clearEnvConfig(),this.clearToken(),R("已清除授权信息,下次登录需要重新授权。"),console.log("\n登出成功!\n")}catch(e){U(`登出失败: ${e.message||e.msg}`),process.exit(1)}else console.log("当前未登录,无需登出。")}async getValidToken(){const e=this.readToken();if(e||(U("未找到授权信息,请先执行 neo login 进行登录。"),process.exit(1)),this.isTokenExpired(e)){console.log("授权信息已过期,正在尝试刷新..."),e.refresh_token||(U("自动刷新授权信息失败,请重新登录(neo login)。"),process.exit(1));const t=await this.refreshToken(e.refresh_token);return t||(U("刷新授权信息失败,请重新登录 (neo login)"),process.exit(1)),this.saveToken(t),t}return e}async getAccessToken(){return await this.getValidToken()}buildFullUrl(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.neoBaseURL}${e}`}}};
|
package/dist/package.json.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.13.
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.13.11";const o={version:e};exports.default=o,exports.version=e;
|
package/package.json
CHANGED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
> **更新时间**:2026-04-01
|
|
2
|
+
> **模块**:商机Pipeline管理
|
|
3
|
+
> **状态**:初步设计
|
|
4
|
+
> **原型参考**:`docs/htmls/forecasting-prototype.html` — Pipeline Tab
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 商机Pipeline管理 — 接口设计文档
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# 一、自定义API相关对象和查询接口
|
|
13
|
+
|
|
14
|
+
本章列出Pipeline功能涉及的核心数据实体及其关键字段,供接口设计和数据建模参考。
|
|
15
|
+
|
|
16
|
+
## 1.1 商机阶段信息
|
|
17
|
+
|
|
18
|
+
Open API:根据商机业务类型查询商机阶段
|
|
19
|
+
|
|
20
|
+
接口URL:`/rest/neobi/v2.0/query/queryByCustomSQL`
|
|
21
|
+
|
|
22
|
+
| 字段 | 类型 | 说明 |
|
|
23
|
+
| --------- | ------ | ------------------------ |
|
|
24
|
+
| id | String | 商机阶段ID |
|
|
25
|
+
| stageName | String | 商机阶段名称 |
|
|
26
|
+
| order | Number | 序号 |
|
|
27
|
+
| status | String | 状态(进行中/输单/赢单) |
|
|
28
|
+
|
|
29
|
+
## 1.2 商机信息
|
|
30
|
+
|
|
31
|
+
Open API:获取销售机会信息
|
|
32
|
+
|
|
33
|
+
接口URL:`/rest/data/v2.0/xobjects/opportunity/{id}`
|
|
34
|
+
|
|
35
|
+
| 字段 | 类型 | 说明 |
|
|
36
|
+
| ---------------- | --------------- | ----------------------------------------------------- |
|
|
37
|
+
| opportunityId | String | 商机ID |
|
|
38
|
+
| opportunityName | String | 商机名称 |
|
|
39
|
+
| accountName | String | 客户名称 |
|
|
40
|
+
| amount | Number | 商机金额 |
|
|
41
|
+
| closeDate | Date | 预计结单日期 |
|
|
42
|
+
| stageId | String | 当前阶段ID |
|
|
43
|
+
| stageName | String | 当前阶段名称 |
|
|
44
|
+
| ownerUserId | String | 商机负责人用户ID |
|
|
45
|
+
| ownerDeptId | String | 商机负责人所属部门ID |
|
|
46
|
+
| businessType | String | 业务类型 |
|
|
47
|
+
| lastActivityDate | DateTime | 最近活动时间 |
|
|
48
|
+
| createdTime | DateTime | 商机创建时间 |
|
|
49
|
+
| winRate | Number | (自定义字段)最终AI赢率(0-100) |
|
|
50
|
+
| baseline | Number | (自定义字段)基线概率(0-100),基于历史同类商机统计 |
|
|
51
|
+
| positiveFactors | Array\<Object\> | (自定义字段)正向因素列表,每项含description和impact |
|
|
52
|
+
| negativeFactors | Array\<Object\> | (自定义字段)负向因素列表,每项含description和impact |
|
|
53
|
+
|
|
54
|
+
## 1.3 自定义实体数据查询
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## 1.4 商机历史快照
|
|
58
|
+
|
|
59
|
+
使用BI提供的自定义SQL查询接口获取快照数据。
|
|
60
|
+
|
|
61
|
+
> Open API:数据平台接口 — 自定义 SQL 查询
|
|
62
|
+
|
|
63
|
+
> 接口URL:`/rest/neobi/v2.0/query/queryByCustomSQL`
|
|
64
|
+
|
|
65
|
+
商机历史快照接口示例
|
|
66
|
+
|
|
67
|
+
select opportunity_1_opportunityName,
|
|
68
|
+
opportunity_1_money
|
|
69
|
+
from biCustomModel_397169_20260401104916618 limit 100
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## 1.5 商机健康度评估信息
|
|
73
|
+
|
|
74
|
+
> Open API:没有Open API,内部可调用业务接口,获取商机健康度结果接口
|
|
75
|
+
|
|
76
|
+
> 接口URL:/rest/ai/v2.0/agent/apps/health_assessment/get_health_assessment_result
|
|
77
|
+
|
|
78
|
+
| 字段 | 类型 | 说明 |
|
|
79
|
+
| ---------------------------- | ------ | ------------------------------------------ |
|
|
80
|
+
| opportunityId | String | 商机ID |
|
|
81
|
+
| level | String | 评分等级:`High` / `Medium` / `Low` |
|
|
82
|
+
| summary | String | AI生成的自然语言摘要,概述商机当前健康状态 |
|
|
83
|
+
| dimensions | Object | 四维评分明细 |
|
|
84
|
+
| dimensions.demand | String | 需求维度评级 |
|
|
85
|
+
| dimensions.decisionChain | String | 决策链维度评级 |
|
|
86
|
+
| dimensions.budgetProcurement | String | 预算和采购流程维度评级 |
|
|
87
|
+
| dimensions.competition | String | 竞争维度评级 |
|
|
88
|
+
|
|
89
|
+
## 1.6 部门人员信息
|
|
90
|
+
|
|
91
|
+
Open API:获取当前用户可见的人员列表
|
|
92
|
+
|
|
93
|
+
接口URL:待确认(平台标准接口)
|
|
94
|
+
|
|
95
|
+
## 1.7 权限内业务类型
|
|
96
|
+
|
|
97
|
+
Open API:获取当前用户权限内的商机业务类型列表及系统默认值
|
|
98
|
+
|
|
99
|
+
接口URL:待确认
|
|
100
|
+
|
|
101
|
+
| 字段 | 类型 | 说明 |
|
|
102
|
+
| -------------------- | --------------- | ------------------------------------------- |
|
|
103
|
+
| businessTypes | Array\<Object\> | 业务类型列表 |
|
|
104
|
+
| businessTypes[].id | String | 业务类型ID |
|
|
105
|
+
| businessTypes[].name | String | 业务类型名称 |
|
|
106
|
+
| defaultBusiType | String | 系统默认业务类型ID(固定为defaultBusiType) |
|
|
107
|
+
|
|
108
|
+
## 1.8 提示词模板调用接口
|
|
109
|
+
|
|
110
|
+
PromptTemplateResponse execute = ScriptAIPromptTemplateService.instance().execute(template);
|
|
111
|
+
int code = execute.getCode();
|
|
112
|
+
String errorMessage = execute.getErrorMsg();
|
|
113
|
+
|
|
114
|
+
## 1.9 查询BI视图数据
|
|
115
|
+
|
|
116
|
+
Open API:按照筛选条件查询视图数据
|
|
117
|
+
|
|
118
|
+
接口URL:`/rest/neobi/v2.0/bestoractices/queryDataTask`
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
# 二、聚合接口设计
|
|
123
|
+
|
|
124
|
+
基于Pipeline Tab各组件的数据需求,设计以下聚合接口。聚合接口在BFF层实现,内部调用第一章的单接口进行数据组装。
|
|
125
|
+
|
|
126
|
+
## 2.0 接口总览
|
|
127
|
+
|
|
128
|
+
| 编号 | 聚合接口 | 触发时机 | 服务的组件 | 调用的单接口 |
|
|
129
|
+
| ----- | -------------------- | -------------------------------- | ----------------------- | --------------------------- |
|
|
130
|
+
| AGG-1 | 获取筛选项初始化数据 | 页面初始化(仅一次) | 全局筛选栏 | 部门架构接口 + 业务类型接口 |
|
|
131
|
+
| AGG-2 | 获取BI视图与AI洞察 | 页面初始化 / 点击✨按钮 | BI视图图表 + AI分析弹窗 | 1.9 + 1.8 |
|
|
132
|
+
| AGG-3 | 获取阶段切换区数据 | 页面初始化 / 筛选变更 | Stage Tabs | 1.1 + 1.2 + 1.4 |
|
|
133
|
+
| AGG-4 | 获取商机列表数据 | 页面初始化 / 筛选变更 / 阶段切换 | 商机明细表格 | 1.2 + 1.5 + 1.6 |
|
|
134
|
+
|
|
135
|
+
## 2.1 AGG-1:获取筛选项初始化数据
|
|
136
|
+
|
|
137
|
+
页面初始化时调用一次,获取筛选栏所需的选项数据和默认值。
|
|
138
|
+
|
|
139
|
+
**请求**:无入参,根据当前登录用户自动获取。
|
|
140
|
+
|
|
141
|
+
**返回**:
|
|
142
|
+
|
|
143
|
+
| 字段 | 类型 | 说明 |
|
|
144
|
+
| --------------- | ------ | ------------------------------------------------------- |
|
|
145
|
+
| currentUser | Object | 当前登录用户信息(userId, name) |
|
|
146
|
+
| departments | Array | 当前用户可见的部门列表(含部门下人员) |
|
|
147
|
+
| businessTypes | Array | 当前用户权限内的业务类型列表 |
|
|
148
|
+
| defaultBusiType | String | 系统默认业务类型ID,用于筛选栏Business Type的默认选中值 |
|
|
149
|
+
|
|
150
|
+
**处理流程**:
|
|
151
|
+
|
|
152
|
+
1. 调用1.7部门人员信息接口,获取当前用户可见的部门架构及人员列表
|
|
153
|
+
2. 调用1.8权限内业务类型接口,获取业务类型列表及系统默认值(defaultBusiType)
|
|
154
|
+
3. 获取当前登录用户信息(userId, name)
|
|
155
|
+
4. 组装返回
|
|
156
|
+
|
|
157
|
+
## 2.2 AGG-2:获取BI视图与AI洞察
|
|
158
|
+
|
|
159
|
+
本接口分为两部分,分步调用。
|
|
160
|
+
|
|
161
|
+
### 2.2.1 BI视图数据
|
|
162
|
+
|
|
163
|
+
页面初始化或筛选变更时调用,分别调用1.9查询两个BI看板的视图数据,以3个筛选项(结单日期、业务类型、所有人)作为入参。对于阶段平均停留时间视图,还需额外调用1.3查询各阶段的基准值和保底值。
|
|
164
|
+
|
|
165
|
+
**BI看板ID**:
|
|
166
|
+
|
|
167
|
+
| 看板 | BI看板ID |
|
|
168
|
+
|------|---------|
|
|
169
|
+
| 商机漏斗(Pipeline Funnel) | 4264464770007375 |
|
|
170
|
+
| 平均停留时间(Avg. Time in Stage) | 4264466340118875 |
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
**处理流程**:
|
|
174
|
+
|
|
175
|
+
1. **商机漏斗**:前端按当前筛选条件组装请求参数,调用1.9接口(viewId=4264464770007375),将返回的数据用于漏斗图渲染(各阶段金额、数量、转化率等)
|
|
176
|
+
2. **平均停留时间**:
|
|
177
|
+
- 调用1.9接口(viewId=4264466340118875),获取各阶段的实际平均停留时间
|
|
178
|
+
- 调用1.3自定义实体数据查询接口,查询自定义对象**Opportunity Stage Plan**(APIKey:customEntity105__c),根据当前商机业务类型opp type+阶段Stage Name,获取各阶段的Target(基准值Reference Advancing Duration)和Limit(保底值Longest Advancing Duration)
|
|
179
|
+
- 前端将实际停留时间与Target/Limit合并渲染条形图
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
### 2.2.2 AI洞察分析
|
|
183
|
+
|
|
184
|
+
点击✨按钮时调用,将2.2.1已获取的BI视图数据作为上下文,调用1.8提示词模板接口生成AI洞察文本。
|
|
185
|
+
|
|
186
|
+
**提示词模板**:API Key = `analyze_BI_insight__c`
|
|
187
|
+
|
|
188
|
+
**模板入参**:
|
|
189
|
+
|
|
190
|
+
| 参数 | 类型 | 说明 |
|
|
191
|
+
|------|------|------|
|
|
192
|
+
| Raw Data | String | BI看板数据(2.2.1返回的columns+rows)+ 基准值/保底值(仅平均停留时间看板需要附加) |
|
|
193
|
+
| business_context | String | 背景说明,根据当前BI看板类型传入固定文案(见下方) |
|
|
194
|
+
|
|
195
|
+
**business_context固定文案**:
|
|
196
|
+
|
|
197
|
+
- **商机漏斗**:`This funnel visualizes the end-to-end sales conversion from each opportunity stage to closed-won, tracking opportunity count, sales amount and stage-by-stage conversion rate across sales stages, to identify pipeline bottlenecks and evaluate sales conversion efficiency.`
|
|
198
|
+
- **平均停留时间**:`This bar chart displays the average time opportunities spend in each sales stage, measured as a percentage of the total sales cycle, to pinpoint process bottlenecks and optimize sales cycle efficiency.`
|
|
199
|
+
|
|
200
|
+
**处理流程**:
|
|
201
|
+
|
|
202
|
+
1. 前端将2.2.1已获取的BI视图数据作为Data传入,避免重复查询;平均停留时间看板需同时附加1.3查询到的Target/Limit数据
|
|
203
|
+
2. 根据当前BI看板类型,传入对应的business_context固定文案
|
|
204
|
+
3. 调用1.8提示词模板接口(API Key: `analyze_BI_insight__c`)执行AI生成
|
|
205
|
+
4. 返回洞察文本,前端展示在AI Insight弹窗中
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
## 2.3 AGG-3:获取阶段切换区数据
|
|
210
|
+
|
|
211
|
+
筛选条件变更时调用,返回Stage Tabs阶段切换区组件的数据(各阶段汇总及变动信息)。
|
|
212
|
+
|
|
|
213
|
+
|
|
214
|
+
**处理流程**:
|
|
215
|
+
|
|
216
|
+
1. 调用1.1商机阶段信息接口,获取当前业务类型的阶段列表(含排序)
|
|
217
|
+
2. 调用1.2商机信息接口,按筛选条件(Close Date、Owner、Business Type)批量查询商机,按阶段分组汇总当前金额和数量
|
|
218
|
+
3. 调用1.4商机历史快照接口,查询changesSince基准日期的快照数据,按阶段分组汇总基准金额和基准数量
|
|
219
|
+
|
|
220
|
+
**历史快照字段参数**:
|
|
221
|
+
|
|
222
|
+
Dataset API Key:biCustomModel_397169_20260401104916618
|
|
223
|
+
|
|
224
|
+
| Field | API Key |
|
|
225
|
+
|-------|---------|
|
|
226
|
+
| ID | id |
|
|
227
|
+
| Amount | opportunity_1_money |
|
|
228
|
+
| Owner department | opportunity_1_dimDepartEntity |
|
|
229
|
+
| Type | opportunity_1_entityType |
|
|
230
|
+
| Forecast Category | opportunity_1_forecastCategory |
|
|
231
|
+
| Status | opportunity_1_status |
|
|
232
|
+
| version | version |
|
|
233
|
+
| Close Date | opportunity_1_closeDate |
|
|
234
|
+
| Recent Activity Record Time | opportunity_1_recentActivityRecordTime |
|
|
235
|
+
| ID | opportunity_1__id |
|
|
236
|
+
| Opportunity | opportunity_1_id |
|
|
237
|
+
| Account Name | opportunity_1_accountId |
|
|
238
|
+
| Sales Stage | opportunity_1_saleStageId |
|
|
239
|
+
| Opportunity Name | opportunity_1_opportunityName |
|
|
240
|
+
| Forecast Category | opportunity_1_ForecastCategory__c |
|
|
241
|
+
| Opportunity Owner | opportunity_1_ownerId |
|
|
242
|
+
4. 金额变动 = 当前阶段金额 − 基准快照阶段金额;数量变动 = 当前阶段数量 − 基准快照阶段数量
|
|
243
|
+
5. 判断变动方向(up/down/none)
|
|
244
|
+
6. 组装stageTabs数据返回
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
## 2.4 AGG-4:获取商机列表数据
|
|
248
|
+
|
|
249
|
+
阶段切换或筛选变更时调用,分两个阶段加载数据:列表首屏加载和Hover按需加载。
|
|
250
|
+
|
|
251
|
+
### 2.4.1 列表首屏加载
|
|
252
|
+
|
|
253
|
+
**处理流程**:
|
|
254
|
+
|
|
255
|
+
1. 调用1.2商机信息接口,按筛选条件+阶段(默认选择第一个阶段)+排序(默认按照销售金额倒叙)+分页查询商机列表,获取所有列表展示字段
|
|
256
|
+
2. AI Score等级标签(level字段)可从商机对象直接获取,无需调用1.6,随列表一并返回
|
|
257
|
+
|
|
258
|
+
### 2.4.2 Hover按需加载
|
|
259
|
+
|
|
260
|
+
以下数据仅在用户鼠标hover时触发,按需调用,避免首屏加载过重。
|
|
261
|
+
|
|
262
|
+
**变动详情(Amount / Close Date / Stage)**:
|
|
263
|
+
|
|
264
|
+
- 触发时机:用户hover某条商机的变动箭头
|
|
265
|
+
- 调用接口:1.5商机历史快照接口
|
|
266
|
+
- 入参:opportunityId + changesSince基准日期
|
|
267
|
+
- 处理:查询该商机在基准日期的快照,与当前实时数据逐字段(Amount/CloseDate/Stage)比对,用当前值-基准日期的值,计算变动方向、变动前后值
|
|
268
|
+
|
|
269
|
+
- 变动时间:调用1.2接口,取商机上的3个时间字段,AmoutChanged、ClosedDateChanged、StageChanged
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
**AI Score详情**:
|
|
275
|
+
|
|
276
|
+
- 触发时机:用户hover某条商机的AI Score标签
|
|
277
|
+
- 调用接口:1.6商机健康度评估信息接口
|
|
278
|
+
- 入参:opportunityId
|
|
279
|
+
- 返回:四维评分及AI摘要summary
|