酷大师插件后端开发指南
- ApiResult
- GeneralModelInfo & GeneralModelData & ResourceTypeEnum
- ExampleController
- ExampleService
- ExampleServiceImpl
如果您认为您的插件需要一个后端服务来实现一些业务逻辑,则可以使用任何后端技术栈来开发这个服务。
酷大师插件后端可以两种方式和酷大师后端交互:
- 酷大师插件后端调用酷大师后端Restful API,获取酷大师方案的3d模型数据
- 酷大师插件后端提供一些WebHook API,供酷大师后端在特定时刻调用
用第一种交互方式,可以实现对酷大师方案中3d模型的后处理。例如:
- 获取酷大师方案中的3d模型,渲染一张高清效果图。
- 获取酷大师方案中的3d模型,进行网格剖分,做力学仿真分析
- 获取酷大师方案中的3d模型,将它3d打印出来
这种交互方式比较简单,主要是理解酷大师后端 REST API 返回的数据格式即可。
用第二种交互方式,可以实现将插件后端定义的3d模型,插入到酷大师方案中。例如酷大师官方内置的“智能模型”,“智能灯箱”等插件,都是将一些插件定义的3d参数化模型,插入到了酷大师方案中。
这种交互方式略微复杂一些,下面用一些Java语言代码来示例说明
ApiResult
定义Web API的返回值类型
ApiResult
    public class ApiResult<T> {
    
        private T resData;
    
        private boolean success;
    
        private int code;
    
        private String msg;
    
        public static <T> ApiResult<T> pack(final T resultData) {
            final ApiResult<T> result = new ApiResult<>();
            result.success = true;
            result.resData = resultData;
            // 0 表示成功
            result.setCode(0);
            return result;
        }
    }
GeneralModelInfo & GeneralModelData & ResourceTypeEnum 
GeneralModelInfo & GeneralModelData
    @Getter
    @Setter
    public class GeneralModelInfo {
        /**
        * 返回状态码
        */
        @JsonProperty("c")
        private int code;
        /**
        * 返回信息体
        */
        @JsonProperty("m")
        private String msg;
        /**
        * 外部模型具体数据
        */
        @JsonProperty("d")
        private GeneralModelData data;
    }
    
    @Getter
    @Setter
    public class GeneralModelData<T> {
        /**
        * @see ResultTypeEnum,这里填ResultTypeEnum.getDesc()
        */
        @JsonProperty("t")
        private String type;
    
        /**
        * resource与上面的type相对应。
        * 如果type是dmxdesigndata,resource则应该是DMxDesignData对象
        * 如果type是renderinfo,resource则应该是mesh包的List<ModelWithMatrix> 、List<DisplayComponent>等类
        * @return
        */
        @JsonProperty("r")
        private T resource;
    }
    
    @Getter
    public enum ResourceTypeEnum {
        RENDER_INFO(0, "renderinfo"),
        DMX_DESIGN_DATA(1, "dmxdesigndata");
    
        private Integer code;
        private String desc;
    
        ResourceTypeEnum(final Integer code, final String desc) {
            this.code = code;
            this.desc = desc;
        }
    }
ExampleController
定义Web API
ExampleController
    @RestController
    public class ExampleController {
    
        private static final String MODEL_USAGE_HEADER = "x-qh-model-usage";
    
    // 以下webhook url可以自定义,与各自的执行函数相对应
        private static final String URL_GET = "/myplugin/model";
        private static final String URL_COPY = "/myplugin/modelindex/copy";
        private static final String URL_DELETE = "/myplugin/modelindex";
        
        // 这个url创建无需注册,但是需要提供给iPaas插件iFrame
        private static final String URL_MODELINDEX_CREATE = "/myplugin/modelindex/create";
    
        @Autowired
        private ExampleService exampleService;
    
        // GetMapping 代表Get请求,后面的url与注册的获取模型的webhook url相对应
        //MODEL_USAGE_HEADER = x-qh-model-usage,从header 中获取,用于定义获取 “精模--0” 还是“粗模--1”(分别用于后端渲染和前端显示)
    @GetMapping(URL_GET)
        public ApiResult<GeneralModelInfo> getModel(
                @RequestParam("modelIndex")
                @NotEmpty(message = "modelIndex can not be empty") final String modelIndex,
                final HttpServletRequest request) {
                final ModelUsageEnum modelUsage =  ModelUsageEnum.getByCodeStr(request.getHeader(MODEL_USAGE_HEADER));        
                return ApiResult.pack(exampleService.buildModel(modelIndex,modelUsage));
        }
    
        // PostMapping 代表Post请求,后面的url与注册的复制模型的webhook url相对应
        // 插件方的复制逻辑可以不做具体实现,但至少要提供一个copy接口签名
        @PostMapping(URL_COPY)
        public ApiResult<String> copyModelIndex(
                @RequestParam("modelIndex")
                @NotEmpty(message = "modelIndex can not be empty") final String modelIndex) {
            return ApiResult.pack(exampleService.copyModel(modelIndex));
        }
    
        // DeleteMapping 代表Delete请求,后面的url与注册的删除模型的webhook url相对应
        // 插件方的删除逻辑可以不做具体实现,但至少要提供一个delete接口签名
        @DeleteMapping(URL_DELETE)
        public ApiResult<String> deleteModelIndex(
                @RequestParam("modelIndex")
                @NotEmpty(message = "modelIndex can not be empty") final String modelIndex) {
            return ApiResult.pack(exampleService.deleteModel(modelIndex));
        }
    
        // param 代表iFrame传过来的参数,按照这套参数,返回一个索引(modelIndex)与之对应。
        // 后续建模,酷大师就根据这个索引来查询模型数据
        @PostMapping(URL_MODELINDEX_CREATE)
        public ApiResult<String> createModelIndex(@RequestBody final T param, final String modelIndex) {
            return ApiResult.pack(exampleService.createModelIndex(param));
        }
    }
ExampleService
实现获取、拷贝、删除模型的业务逻辑
ExampleService
    public interface ExampleService {
        /**
        * 通过modelIndex获取模型
        * @param modelIndex
        * @param modelUsage
        * @return
        */
        GeneralModelInfo buildModel(final String modelIndex,final ModelUsageEnum modelUsage);
    
        /**
        * 拷贝modelIndex
        * @param modelIndex
        * @return
        */
        String copyModelIndex(final String modelIndex);
    
        /**
        * 删除modelIndex
        * @param modelIndex
        * @return
        */
        String deleteModelIndex(final String modelIndex);
        
        /**
        * 创建modelIndex
        * @param param
        * @return
        */
        String createModelIndex(final T param);
    }
ExampleServiceImpl
ExampleServiceImpl
    @Component
    public class ExampleServiceImpl implements ExampleService {
    
        @Override
        // buildModel的核心逻辑是根据modelIndex构建模型,并转化为酷大师需要的数据格式
        public GeneralModelInfo buildModel(final String modelIndex,final ModelUsageEnum modelUsage) {
            // todo
            
            // 根据modelIndex 构建自己的模型
            // MyPluginModel 类是用户自己定义的类
            MyPluginModel myPluginModel = buildModel(modelIndex);
    
            // 由于酷大师后端目前只支持dmxdesigndata和displayinfo两种类型,因此插件后端需要实现将自己的模型转换为这两种类型
            GeneralModelInfo result = new GeneralModelInfo();
            
            if(RENDER_INFO.equeals(modelUsage){
            DMxDesignData designData = convert2DesignData(myPluginModel);
                result.setData(designData);
            }else{
                RenderInfo = convert2RenderInfo(myPluginModel);
                result.setData(designData);
            }
            
            return result;
        }
    
        /**
        * 如果不提供复制操作,可以不做业务逻辑的编写,返回null即可;如果需要实现,建议复制一份modelIndex即可,不需要复制modelIndex所指向的数据本身,以免影响其他同样使用了该数据的方案
        * @param modelIndex
        * @return
        */
        @Override
        public String copyModelIndex(final String modelIndex) {
            final String copiedModelIndesx = copyModel(modelIndex);
            return  copiedModelIndesx;
        }
    
        /**
        * 如果不提供删除操作,可以不做业务逻辑的编写,返回null即可;如果需要实现,建议删除该modelIndex即可,不需要删除modelIndex所指向的数据本身,以免影响其他同样使用了该数据的方案
        * @param modelIndex
        * @return
        */
        @Override
        public String deleteModelIndex(final String modelIndex) {
            // todo
            return null;
        }
    
        private PluginModel buildModel(final String modelIndex) {
            // todo
            return null;
        }
    
        private DMxDesignData convert2DesignData(final PluginModel pluginModel) {
            // todo
            return null;
        }
    }
最后,插件后端定义好的 Web Hook API, 需要用酷大师插件开发教程提及的 WebHook 注册页面工具,或 WebHook API 提及的 WebHook 注册 REST API,注册到酷大师后端