对于只是自己产品内部使用的打印程序来说(比如打印收费单,打印结算单等),打印逻辑写在js,获取其他层都是没毛病的。但是对于类型检验报告这种打印来说,打印格式控制逻辑写在js层是百分百不行的。因为检验报告的打印不光检验内部用,还要给his、自助机、病案、支付宝、微信等用,你不可能要求第三方都有js执行环境。所以逻辑写在js的方案可以直接pass。
同时由于报告格式多样性和经常调整,把打印格式控制逻辑直接写在dll也是不合适的,你总不能改个格式就要求his、自助机等更新你的dll。
所以最完美的方案就是给第三方提供文件或者提供不包含业务逻辑的dll,对外dll只负责按元素绘制执行打印,内部不包含任何业务逻辑,dll通过数据库连接串调用服务器的脚步得到绘制数据绘制,这样改格式不影响第三方使用,在自己服务器内部就能完成打印格式控制。
设计参照打印设计
这种设计方案虽然很完美,但是也得有类似M的服务器脚本支撑,用Cache数据库完全没问题,换到关系库由于没有M的存在,所以这个方案就不行了,为此设计虚拟M层。
虚拟M层旨在提供一个类型M的脚步层,在虚拟M层,开发人员可以写和M的Query一样的逻辑,同时提供脚本化的效果,这样就可以借助虚拟M层完成很多事情,比如:控制输出打印元素、查询excel导出数据、启动TCP连接设备等,虚拟M的前提是框架实现脚本化。
首先实现虚拟M的主入口,到时候使用的地方就配置该ashx的路径来调用虚拟M
import LIS.Core.Dto.OutParam;
import LIS.Core.MultiPlatform.LISContext;
import LIS.Model.Bussiness.Parameters;
import appcode.BaseHttpHandlerNoSession;
import appcode.Helper;
import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Method;
/**
* 虚拟M层的调用入口实现类,由该类对外提供http的虚拟M服务。通过java业务脚本实现和M方法或者Query一样的输入输出约定,来供业务调用,进而基于
* 此基础上实现打印和导出Excel等功能,vm概念很重要,打印、导出、连仪器都离不开
*/
public class VMService extends BaseHttpHandlerNoSession {
/**
* 供外部http调用的请求入口
*
* @return
*/
public String GetData() {
//类名
String ClassName = Helper.ValidParam(LISContext.GetRequest(Request, "ClassName"), "");
//方法名
String FuncName = Helper.ValidParam(LISContext.GetRequest(Request, "FuncName"), "");
//参数
String Param = Helper.ValidParam(LISContext.GetRequest(Request, "Param"), "");
//会话串
String Session = Helper.ValidParam(LISContext.GetRequest(Request, "Session"), "");
//调用虚拟M代码
return GetVMData(ClassName, FuncName, Param, Session);
}
/**
* 得到数据,调用者和Web在一个程序预时候直接通过接口调用,避免post的性能损失
*
* @param ClassName 类名 vm.his.printbarcode
* @param FuncName 方法名 GetData
* @param Param P0-P14的参数json串
* @param Session 会话串
* @return
*/
public String GetVMData(String ClassName, String FuncName, String Param, String Session) {
StringBuilder objStream = new StringBuilder();
try {
PrintWriter writer = Response.getWriter();
String[] funArr = FuncName.split("^");
String[] sessArr = Session.split("^");
String FunModul = "";
if (funArr.length > 1) {
FunModul = funArr[1];
}
String RowCount = "";
FuncName = funArr[0];
String OutPutType = "";
if (sessArr.length > 6) {
OutPutType = sessArr[6];
}
Parameters ParamObj = (Parameters) Helper.Json2Object(Param, Parameters.class);
//转换M的类名为java的类名称
ClassName = LISContext.WebBasePath + File.separator + ClassName.replace(".", File.separator);
//反射得到类型
Object objDeal = GetBllObjService.GetObjectByConfString(ClassName, writer, "", "");
//没有实现类
if (objDeal == null) {
String errStr = "类" + ClassName + "不存在";
//编译报错信息
if (GetBllObjService.BuildingResHash.containsKey(ClassName)) {
errStr = GetBllObjService.BuildingResHash.get(ClassName).toString();
}
objStream.append("<Response>");
objStream.append("<SQLResult><SQL><FunRet></FunRet></SQL></SQLResult><RetVal>-1</RetVal><Error>" + errStr + "</Error><Node>" + ClassName + "</Node><RowCount>0</RowCount>");
objStream.append("</Response>");
return objStream.toString();
}
//获得类型
Class type = objDeal.getClass();
//得到方法
Method method = type.getMethod(FuncName);
//没有实现方法
if (method == null) {
objStream.append("<Response>");
objStream.append("<SQLResult><SQL><FunRet></FunRet></SQL></SQLResult><RetVal>-1</RetVal><Error>" + ClassName + "里面不存在:" + FuncName + "方法</Error><Node>" + ClassName + "</Node><RowCount>0</RowCount>");
objStream.append("</Response>");
return objStream.toString();
}
//会话对象,可能带出数据
OutParam SessionOut=new OutParam();
SessionOut.Message=Session;
//返回行数对象,可能带出数据
OutParam RowCountOut=new OutParam();
RowCountOut.Message=RowCount;
Object[] paraObj = new Object[16];
paraObj[0] = ParamObj.P0;
paraObj[1] = ParamObj.P1;
paraObj[2] = ParamObj.P2;
paraObj[3] = ParamObj.P3;
paraObj[4] = ParamObj.P4;
paraObj[5] = ParamObj.P5;
paraObj[6] = ParamObj.P6;
paraObj[7] = ParamObj.P7;
paraObj[8] = ParamObj.P8;
paraObj[9] = ParamObj.P9;
paraObj[10] = ParamObj.P10;
paraObj[11] = ParamObj.P11;
paraObj[12] = ParamObj.P12;
paraObj[13] = ParamObj.P13;
paraObj[14] = SessionOut;
paraObj[15] = RowCountOut;
objStream.append("<Response>");
int ResType = 0;
String retJson = "";
String Err = "";
//调用方法直接返回串
if (FuncName.substring(FuncName.length() - 4) == "MTHD" || FunModul == "MTHD") {
//执行返回数据
Object retObj = method.invoke(objDeal, paraObj);
Session = SessionOut.GetString();
RowCount = RowCountOut.GetString();
retJson = retObj.toString();
ResType = 1;
}
//方法直接输出Json串
else if (FuncName.length() > 10 && FuncName.substring(FuncName.length() - 10) == "JSONStream" || FunModul == "JSONStream") {
//执行返回数据
Object retObj = method.invoke(objDeal, paraObj);
Session = SessionOut.GetString();
RowCount = RowCountOut.GetString();
ResType = 1;
retJson = retObj.toString();
objStream.append("<" + FuncName + "Result>");
objStream.append(DealXml(retJson));
objStream.append("</" + FuncName + "Result>");
objStream.append("<RetVal>0</RetVal><Error></Error><Node>" + FuncName + "</Node><RowCount>" + RowCount + "</RowCount><ResType>" + ResType + "</ResType>" + "<RetSession>" + Session + "</RetSession>");
objStream.append("</Response>");
return objStream.toString();
}
//方法输出List顶替Query
else {
//执行返回数据
Object retObj = method.invoke(objDeal, paraObj);
Session = SessionOut.GetString();
RowCount = RowCountOut.GetString();
retJson = retObj.toString();
if (FuncName.substring(FuncName.length() - 4) == "JSON") {
ResType = 1;
} else {
ResType = 2;
}
}
objStream.append("<" + FuncName + "Result>" + DealXml(retJson) + "</" + FuncName + "Result>");
objStream.append("<RetVal>0</RetVal><Error>" + Err + "</Error><Node>" + FuncName + "</Node><RowCount>" + RowCount + "</RowCount><ResType>" + ResType + "</ResType>" + "<RetSession>" + Session + "</RetSession>");
objStream.append("</Response>");
return objStream.toString();
} catch (Exception ex) {
objStream.append("<Response>");
objStream.append("<SQLResult><SQL><FunRet></FunRet></SQL></SQLResult><RetVal>-1</RetVal><Error>调用:" + DealXml(ClassName + ",方法:" + FuncName + ",参数:" + Param + ",会话:" + Session + "发生异常!" + Helper.Object2Json(ex)) + "</Error><Node>" + ClassName + "</Node><RowCount>0</RowCount>");
objStream.append("</Response>");
return objStream.toString();
}
}
/**
* 替换xml干扰串
*
* @param str
* @return
*/
private String DealXml(String str) {
return str.replace("&", "&").replace("'", "'").replace("\"", """).replace(">", ">").replace("<", "<");
}
}
然后实现两个虚拟M的示例
打印初步结构
import LIS.Core.Dto.OutParam;
import appcode.BaseHttpHandlerNoSession;
/**
* 输出符合打印元素绘制协议的打印元素数据,来供打印执行层打印
*/
public class PrintBarCodeTest extends BaseHttpHandlerNoSession {
/**
* 按传入的RowID输出符合打印元素绘制协议的数据来实现打印控制
* @param RowID 业务主键
* @param P1
* @param P2
* @param P3
* @param P4
* @param P5
* @param P6
* @param P7
* @param P8
* @param P9
* @param P10
* @param P11
* @param P12
* @param P13
* @param Session
* @param Output
* @return
*/
public String GetData(String RowID, String P1, String P2, String P3, String P4, String P5, String P6, String P7, String P8, String P9, String P10, String P11, String P12, String P13, OutParam Session, OutParam Output) {
//先包一个打印元素绘制协议数据生成的工具类来解决元素约束问题,工具类实现了调用工具类得到标签,条码,文本等
return "[]";
}
}
导出的示例
import LIS.Core.Dto.OutParam;
import LIS.Model.Entity.BTTestCode;
import appcode.BaseHttpHandlerNoSession;
import appcode.Helper;
import java.util.List;
/**
* 输出符合Query约定的数据供导出Excel用,所有的虚拟M方法参数约定就是这个样子
*/
public class ExportExcelTest extends BaseHttpHandlerNoSession {
/**
* 查询所有项目数据导出到Excel
* @param P0
* @param P1
* @param P2
* @param P3
* @param P4
* @param P5
* @param P6
* @param P7
* @param P8
* @param P9
* @param P10
* @param P11
* @param P12
* @param P13
* @param Session
* @param Output
* @return
*/
public String QryTestCode(String P0, String P1, String P2, String P3, String P4, String P5, String P6, String P7, String P8, String P9, String P10, String P11, String P12, String P13, OutParam Session, OutParam Output) throws Exception{
BTTestCode dto=new BTTestCode();
//返回的参数,供Excel模板使用
Session.Message="项目数据导出^"+LIS.Core.Util.TimeParser.GetNowDate()+"^zhanglianzhu";
//查询项目数据
List<BTTestCode> retList=EntityManager().FindAll(dto,null,"",-1,-1);
//数组转json就是等价Query的
return Helper.Object2Json(retList);
}
}
这样客户端执行层再对接虚拟M调用后就可以完成以前在M层实现的效果了,为了支持虚拟M层,把业务脚本化的实现下沉到jar包层,虚拟M和Web调用共用业务脚本化基础
虚拟M存放的目录