通用的异常处理程序机制与处理返回值方案

news2024/11/13 16:18:02

通用的异常处理程序机制与返回值方案

文章目录

  • 通用的异常处理程序机制与返回值方案
  • 现状
    • 示例
  • 方案
    • 原理
    • 步骤
  • 总结
  • 完整代码

现状

  • 相信很多人都为处理错误返回值代码都烦恼过。例如:一个程序嵌套了10个方法,嵌套最深的方法一旦有个业务错误代码,那么后续的程序都要在返回值进行判断。
  • 当程序发生系统错误时,例如变量未定义等。也没有记录错误的日志,也不会记录当时报错程序的类、方法,参数等信息。或者说只记录$ze的信息。
  • 当一个无法复现的异常时或者小概率错误时,没有一个机制可以查看调用流程和记录流程所有变量的信息。

示例

示例模拟登录后支付处理库存简单的业务操作流程。

  • 登录Login->支付Pay->库存Stock

先看一下大多数返回值的几种写法:

  1. 登录方法用^分割来处理错误代码。
ClassMethod LoginLogic(num)
{
	// todo 模拟登录逻辑
	q:(num = 1) "-1^输入密码错误"
	q:(num = 2) "-2^账户不存在"
	q:(num = 3) "-3^用户没有启用"
	q 0
}

这是我们最常见的错误返回值代码。这种返回值有以下问题:

  • 需要^分隔符来分割,当分隔符不存在的时候,会出现错误。
  • 当返回值中有多余的分隔符时,会导致返回值按位置取值错误。
  • 每一次需要先按分隔符分割,再判断代码是否正确,方法层数多了很繁琐。
  • 如果其他方法不按此规则返回错误,则会发生异常。例如有写方法直接返回SQLCODE
USER>w ##class(M.OldHandleError).LoginLogic($random(4))
-3^用户没有启用
USER>w ##class(M.OldHandleError).LoginLogic($random(4))
-2^账户不存在
USER>w ##class(M.OldHandleError).LoginLogic($random(4))
-1^输入密码错误
USER>w ##class(M.OldHandleError).LoginLogic($random(4))
0
  1. 支付方法直接返回错误描述。
  • 返回值判断比按分隔符方便一些,只需要判断不等于0,即可判断是否成功。
ClassMethod PayLogic(num)
{
	// todo 模拟支付逻辑
	q:(num = 4) "余额不足"
	q:(num = 5) "单笔支付过高"
	q:(num = 6) "重复支付"
	q 0
}

这种返回值有以下问题:

  • 返回处理返回ID时,就得需要把判断改成+字符串等于0,也比较繁琐。
USER>w ##class(M.OldHandleError).PayLogic($random(7))
重复支付
USER>w ##class(M.OldHandleError).PayLogic($random(7))
余额不足
USER>w ##class(M.OldHandleError).PayLogic($random(7))
单笔支付过高
USER>w ##class(M.OldHandleError).PayLogic($random(7))
0
  1. 处理库存方法直接返回JSON处理错误描述。
  • 使用JSON规避了分隔符问题,也规避了判断不一直的方式。
ClassMethod StockLogic(num)
{
	// todo 模拟库存逻辑
	q:(num = 7) {"code":-7,"msg":"库存不足"}
	q:(num = 8) {"code":-8,"msg":"批次不存在"}
	q:(num = 9) {"code":-9,"msg":"订单不存在"}
	q {"code":0,"msg":"订单不存在"}
}

这种返回值有以下问题:

  • 每次方法都要组装对象,方法层数多,每次也都需要判断很繁琐。
  • 如果有不按JSON作为返回,也会发生异常。
USER>w ##class(M.OldHandleError).StockLogic($random(9)).%ToJSON()
{"code":-9,"msg":"订单不存在"}
USER>w ##class(M.OldHandleError).StockLogic($random(9)).%ToJSON()
{"code":-7,"msg":"库存不足"}
USER>w ##class(M.OldHandleError).StockLogic($random(9)).%ToJSON()
{"code":-8,"msg":"批次不存在"}
USER>w ##class(M.OldHandleError).StockLogic($random(9)).%ToJSON()
{"code":0,"msg":"成功"}
  1. 模拟业务方法。
  • 如果业务逻辑,每个方法的返回值处理都不一致。那么主业务逻辑的方法看起来会非常的混乱。
  • 一旦发生报错也非常不好定位。
  • 陷阱错误处理程序也没有记录错误信息和堆栈信息。无法定位和复原当时的情况。
  • 这里还不包括其他返回值的情况。例如返回SQLCODEsc等。
ClassMethod MainLogic(num = {$random(11)})
{
	s $zt = "Error"
	s ret = ..LoginLogic(num)
	#; 需要判断分割字符串
	s code = $p(ret, "^", 1)
	s msg = $p(ret, "^", 2)
	q:(code < 0) msg
	
	s ret = ..PayLogic(num)
	#; 需要判断是否为0
	q:(ret '= 0) ret
	
	s ret = ..StockLogic(num)
	#; 需要判断是为JSON对象
	q:('$isobject(ret)) "异常错误"
	q:(ret.code <0 ) ret.msg
	
	q $$$OK
Error
    s $zt = ""
	q $ze
}

USER> w ##class(M.OldHandleError).MainLogic()
输入密码错误
USER> w ##class(M.OldHandleError).MainLogic()
1
USER> w ##class(M.OldHandleError).MainLogic()
批次不存在
USER> w ##class(M.OldHandleError).MainLogic()
单笔支付过高
USER> w ##class(M.OldHandleError).MainLogic()
库存不足
USER> w ##class(M.OldHandleError).MainLogic()
余额不足

方案

基于以上问题我们来设计一套通用的解决方案:

  • 一次性解决返回值的判断问题,方法不用每次在判断上个方法的返回值。只需要接收正确的返回值结果。
  • 记录异常信息或报错方法的类,方法,参数,堆栈信息。
  • 利用系统日志记录错误,查看整个堆栈的环境变量信息。

原理

通过Throw命令抛出自定义异常,由统一入口(门面)拦截抛出的信息,进行处理,根据不同的异常信息,可定制成功消息,失败消息,提示消息。

统一入口(门面模式)的异常处理程序,进行记录系统日志与堆栈信息。

步骤

  1. 定义M.BaseException异常基类。
  • M.BaseException继承%Exception.AbstractException
  • 添加如下AsSystemError()方法,该方法把异常信息处理为$ze格式。
Class M.BaseException Extends %Exception.AbstractException
{

Method AsSystemError() As %String [ CodeMode = expression ]
{
i%Name_i%Location_$select(i%Data'="":$select($extract(i%Data)="^":" ",1:" *")_i%Data,1:"")
}

}
  1. 分别定义登录、支付、库存异常类继承M.BaseException
  • M.WarningException - 用于错误提示信息,并非错误。
Class M.WarningException Extends M.BaseException
{
    
}
  • M.LoginException - 用于处理登录错误。
Class M.LoginException Extends M.BaseException
{

}
  • M.PayException - 用于处理支付错误。
Class M.PayException Extends M.BaseException
{

}
  • M.StockException- 用于处理库存错误。
Class M.StockException Extends M.BaseException
{

}
  1. 定义M.Base.inc文件,定义一些处理异常的宏。
  • 目的是省去书写大量重复的代码与重复的参数,如下代码只需要传入错误描述即可。
#define StockException(%str) ##class(M.StockException).%New("处理库存失败", -102, ,%str)

#define PayException(%str) ##class(M.PayException).%New("支付失败", -101, ,%str)

#define LoginException(%str) ##class(M.LoginException).%New("登录失败", -100, ,%str)

#define WarningException(%msg) ##class(M.WarningException).%New("提示", -100, ,%msg)

#define SystemException(%name, %msg) ##class(%Exception.SystemException).%New(%name, -100, ,%msg)
  1. 分别修改登录、支付、库存处理返回值程序。
  • 这里模拟了提示信息与错误信息,提示信息用$$$WarningException返回,错误信息用对应的业务异常类来处理。

  • 通过num变量的随机数模拟真实业务场景可能发生的错误。

  • LoginLogic

ClassMethod LoginLogic(num)
{
    // todo 模拟登录逻辑
	throw:(num = 1) $$$LoginException("输入密码错误")
	throw:(num = 2) $$$WarningException("账户不存在")
	throw:(num = 3) $$$LoginException("用户没有启用")
	q $$$OK
}
  • PayLogic
ClassMethod PayLogic(num)
{
    // todo 模拟支付逻辑
	throw:(num = 4) $$$PayException("余额不足")
	throw:(num = 5) $$$PayException("单笔支付过高")
	throw:(num = 6) $$$WarningException("重复支付")
	q $$$OK
}
  • LoginLogic
ClassMethod StockLogic(num)
{
    // todo 模拟库存逻辑
	throw:(num = 7) $$$WarningException("库存不足")
	throw:(num = 8) $$$StockException("批次不存在")
	throw:(num = 9) $$$StockException("订单不存在")
	q $$$OK
}

  1. 主业务逻辑
  • 我们发现主业务逻辑没有处理异常信息,仅处理正确返回值。因为调用主逻辑方法需要一个通用的门面入口,所以不必要再此定义异常处理程序。
ClassMethod MainLogic(num = {$random(11)})
{
	s ret = ..LoginLogic(num)
	s ret = ..PayLogic(num)
	s ret = ..StockLogic(num)
	q ret
}
  1. 定义通用返回JSON处理方法,作为处理返回值。
  • 定义返回成功的消息,失败的消息,提示的消息。
/// 返回消息Json
ClassMethod Msg(code, msg = "", data = "") As %DynamicObject
{
    s ret = {}
    s ret.code = code
    s ret.msg = msg
    s ret.data = data
    q ret
}

/// 返回前台的成功消息
ClassMethod Success(data) As %DynamicObject
{
    q ..Msg(200, "", data)
}

/// 返回前台的成功消息转Json
ClassMethod Success2Json(data) As %String
{
    q ..Success(data).%ToJSON()
}

/// 返回前台的失败消息
ClassMethod Failure(msg, data = "") As %DynamicObject
{
    q ..Msg(-1, msg, data)
}

/// 返回前台的失败消息转Json
ClassMethod Failure2Json(msg, data = "") As %String
{
    q ..Failure(msg, data).%ToJSON()
}

/// 返回前台的警告消息
ClassMethod Warning(msg, data = "") As %DynamicObject
{
    q ..Msg(0, msg, data)
}

/// 返回前台的警告消息转Json
ClassMethod Warning2Json(msg, data = "") As %String
{
    q ..Warning(msg, data).%ToJSON()
}

  1. 定义保存日志方法。
  • 这里我们保存日志的信息使用的txt文件。txt文件的好处是:当服务器库发生崩溃打不开时,txt文件可以不受干扰。
  • 也可以建立表保存到数据库。根据情况适当选择。
  • 先指定Filename保存的txt文件名,将获取的类、方法、参数值和根据$Stack来获取的堆栈信息,保存追加到txt文件。
Parameter Filename = "E:\m\error.txt";

ClassMethod LogErrors(msg, className, methodName, params...)
{
	s stream=##class(%FileCharacterStream).%New()
	s stream.Filename = ..#Filename
	d stream.MoveToEnd()
	
	d stream.WriteLine("----------------------------------") 
	s str = "日期:" _ $zdt($h, 3)
	d stream.WriteLine(str) 
	
	s str = "类:" _ className
	d stream.WriteLine(str) 
	s str = "方法:" _ methodName
	d stream.WriteLine(str) 
	
	s str = "参数:" _ ..GetMethodParams(className, methodName, params...)
	d stream.WriteLine(str) 
	
	s str = "错误信息:" _ msg
	d stream.WriteLine(str) 
	
	s str = "堆栈信息:"
	d stream.WriteLine(str) 
	
	for loop = 0 : 1 : $stack(-1) { 
		s place = $stack(loop, "PLACE")
		s source = $stack(loop, "MCODE")
		s ecode = $stack(loop, "ECODE")
		s ecode = $s(ecode '= "" : ", ecode: " _ ecode,1:"")
		s info =  " [place: " _ place _ ", source: "_ ..Trim(source) _ ecode _"]"
		d stream.WriteLine(info)
	}
	d stream.WriteLine("----------------------------------") 
	d stream.WriteLine("")
	d stream.WriteLine("")
	d stream.SaveStream()
	d stream.%Close()
}
  1. 创建调用门面。
  • 门面是统一入口,处理返回值与异常信息,保存系统日志与txt文件。
  • 根据不同异常,返回信息处理不用,提示异常可直接返回,错误异常可记录到日志,成功直接返回。

注:使用Try-Catch$Ztrap双保险机制,在Try-Catch里处理复杂的异常日志保存程序时可能会发生错误,例如插入日志表时字段长度不够,使用$ZtrapTry-Catch复杂的异常处理程序进行保险,保证程序捕捉所有异常。

ClassMethod GateWay(className, methodName, params...)
{
	#; 清除错误信息
	s $ec = ""
	s $ze = ""
	
	#; 设置陷阱
	s $zt = "Error"
	try {
	
		#; 调用门面
		s ret = $classmethod(className, methodName, params...)
		
	} catch e {

		#; 解锁当前进程加的异常锁
	    lock
	    
	    #; 回滚异常事务
	    tro:($tl > 0)
	    
	    #; 提示信息,按需做日志记录,此处没有保存错误日志与堆栈
		if (e.%IsA("M.WarningException")){
		    s msg = e.AsSystemError()
			ret ..Warning2Json(msg)
		}
	    
	    #; 记录系统日志
	    s data = e.Data
	    s:$lv(data) data = $$Format^%qcr(data, 1)
	    s msg = e.Name _ e.Location _ " *" _ data
		s ret = $$LOG^%ETN(msg)
	    s id = $list(ret, 2)
	
		#; 记录txt日志以及堆栈
		d ..LogErrors(msg, className, methodName, params...)
		
		#; 将业务错误信息返回
		if (e.%IsA("M.BaseException")){
		    s msg = e.AsSystemError()
			ret ..Failure2Json(msg _ " 错误日志id:" _ id)
		}
		
		#; 其他错误
		if (msg = "") {
			s msg = $ze
		}
		ret ..Failure2Json(msg _ " 错误日志id:" _ id)
		
	}
	
	#; 成功消息
	ret ..Success2Json(ret)
Error
	s $zt = ""
	q ..Failure2Json("意外错误:" _ $ze)
}
  • 入口方法为统一调用门面方法。其他语言称之为反射。
USER>w ##class(M.GateWay).GateWay("M.Main","MainLogic",$random(11))
{"code":-1,"msg":"登录失败 *输入密码错误 错误日志id:49","data":""}
  • 查看txt文件。

在这里插入图片描述

  • 包含报错程序的,时间,类,方法,参数,错误信息,堆栈信息。这些最简单的信息。需要其他信息根据需要进行添加

在这里插入图片描述

  • 查看系统错误日志

在这里插入图片描述

  • 查看系统日志环境变量

在这里插入图片描述

  • 查看系统日志堆栈信息

在这里插入图片描述

总结

按照上述方式即可达到:

  1. 一次性解决返回值的判断问题,方法不用每次在判断上个方法的返回值。只需要接收正确的返回值结果。
  2. 记录异常信息或报错方法的类,方法,参数,堆栈信息。
  3. 利用系统日志记录错误,查看整个堆栈的环境变量信息。
  4. 示例门面代码可直接复制到你的程序中作为入口使用,调用的方法再也不需要判断返回值了,也不用再给每个方法设置专门的异常处理程序了。

  • $Ztrap方式捕捉异常。
ClassMethod GateWayZtrap(className, methodName, params...)
{
	s $ec = ""
	s $ze = ""
	s $zt = "Error"

	s ret = $classmethod(className, methodName, params...)
	ret ..Success2Json(ret)

	q $$$OK
Error
	
	s $zt = ""
    lock
    tro:($tl > 0)
    
    s e = $throwobj
    
    #; 提示信息,按需做日志记录,此处没有保存错误日志与堆栈
	if (e.%IsA("M.WarningException")){
	    s msg = e.AsSystemError()
		ret ..Warning2Json(msg)
	}
    
    #; 记录系统日志
    s data = e.Data
    s:$lv(data) data = $$Format^%qcr(data, 1)
    s msg = e.Name _ e.Location _ " *" _ data
	s ret = $$LOG^%ETN(msg)
    s id = $list(ret, 2)

	#; 记录txt日志以及堆栈
	d ..LogErrors(msg, className, methodName, params...)
	

	if (e.%IsA("M.BaseException")){
	    s msg = e.AsSystemError()
		ret ..Failure2Json(msg _ " 错误日志id:" _ id)
	}
	if (msg = "") {
		s msg = $ze
	}
	ret ..Failure2Json(msg _ " 错误日志id:" _ id)
}
USER>w ##class(M.GateWay).GateWayZtrap("M.Main","MainLogic",$random(11))
{"code":0,"msg":"提示 *库存不足","data":""}
USER>w ##class(M.GateWay).GateWayZtrap("M.Main","MainLogic",$random(11))
{"code":200,"msg":"","data":1}
USER>w ##class(M.GateWay).GateWayZtrap("M.Main","MainLogic",$random(11))
{"code":-1,"msg":"登录失败 *用户没有启用 错误日志id:51","data":""}
USER>w ##class(M.GateWay).GateWayZtrap("M.Main","MainLogic",$random(11))
{"code":-1,"msg":"支付失败 *单笔支付过高 错误日志id:52","data":""}

在这里插入图片描述

在这里插入图片描述

完整代码

  • M.GateWay
Class M.GateWay Extends %RegisteredObject
{
/// 二者取其一 - 双保险方式
/// w ##class(M.GateWay).GateWay("M.Main","MainLogic",$random(11))
ClassMethod GateWay(className, methodName, params...)
{
	#; 清除错误信息
	s $ec = ""
	s $ze = ""
	
	#; 设置陷阱
	s $zt = "Error"
	try {
		
		#; 调用门面
		s ret = $classmethod(className, methodName, params...)

	} catch e {
		
		#; 通过throw抛出的异常,$stack捕获不到,是因为$ec与$ze为空
		#; 解锁当前进程加的异常缩
	    lock
	    
	    #; 回滚异常事务
	    tro:($tl > 0)
	    
	    #; 提示信息,按需做日志记录,此处没有保存错误日志与堆栈
		if (e.%IsA("M.WarningException")){
		    s msg = e.AsSystemError()
			ret ..Warning2Json(msg)
		}
	    
	    #; 记录系统日志
	    s data = e.Data
	    s:$lv(data) data = $$Format^%qcr(data, 1)
	    s msg = e.Name _ e.Location _ " *" _ data
		s ret = $$LOG^%ETN(msg)
	    s id = $list(ret, 2)
	
		#; 记录txt日志以及堆栈
		d ..LogErrors(msg, className, methodName, params...)
		
		#; 将业务错误信息返回
		if (e.%IsA("M.BaseException")){
		    s msg = e.AsSystemError()
			ret ..Failure2Json(msg _ " 错误日志id:" _ id)
		}
		
		#; 其他错误
		if (msg = "") {
			s msg = $ze
		}
		ret ..Failure2Json(msg _ " 错误日志id:" _ id)
		
	}
	
	#; 成功消息
	ret ..Success2Json(ret)
Error
	s $zt = ""
	q ..Failure2Json("意外错误:" _ $ze)
}
/// 二者取其一 - Ztrap方式
/// w ##class(M.GateWay).GateWayZtrap("M.Main","MainLogic",$random(11))
ClassMethod GateWayZtrap(className, methodName, params...)
{
	s $ec = ""
	s $ze = ""
	s $zt = "Error"

	s ret = $classmethod(className, methodName, params...)
	ret ..Success2Json(ret)

	q $$$OK
Error
	
	s $zt = ""
    lock
    tro:($tl > 0)
    
    s e = $throwobj
    
    #; 提示信息,按需做日志记录,此处没有保存错误日志与堆栈
	if (e.%IsA("M.WarningException")){
	    s msg = e.AsSystemError()
		ret ..Warning2Json(msg)
	}
    
    #; 记录系统日志
    s data = e.Data
    s:$lv(data) data = $$Format^%qcr(data, 1)
    s msg = e.Name _ e.Location _ " *" _ data
	s ret = $$LOG^%ETN(msg)
    s id = $list(ret, 2)

	#; 记录txt日志以及堆栈
	d ..LogErrors(msg, className, methodName, params...)
	

	if (e.%IsA("M.BaseException")){
	    s msg = e.AsSystemError()
		ret ..Failure2Json(msg _ " 错误日志id:" _ id)
	}
	if (msg = "") {
		s msg = $ze
	}
	ret ..Failure2Json(msg _ " 错误日志id:" _ id)
}

Parameter Filename = "E:\m\error.txt";

ClassMethod LogErrors(msg, className, methodName, params...)
{
	s stream=##class(%FileCharacterStream).%New()
	s stream.Filename = ..#Filename
	d stream.MoveToEnd()
	
	d stream.WriteLine("----------------------------------") 
	s str = "日期:" _ $zdt($h, 3)
	d stream.WriteLine(str) 
	
	s str = "类:" _ className
	d stream.WriteLine(str) 
	
	s str = "方法:" _ methodName
	d stream.WriteLine(str) 
	
	s str = "参数:" _ ..GetMethodParams(className, methodName, params...)
	d stream.WriteLine(str) 
	
	s str = "错误信息:" _ msg
	d stream.WriteLine(str) 
	
	s str = "堆栈信息:"
	d stream.WriteLine(str) 
	
	for loop = 0 : 1 : $stack(-1) { 
		s place = $stack(loop, "PLACE")
		s source = $stack(loop, "MCODE")
		s ecode = $stack(loop, "ECODE")
		s ecode = $s(ecode '= "" : ", ecode: " _ ecode,1:"")
		s info =  " [place: " _ place _ ", source: "_ ..Trim(source) _ ecode _"]"
		d stream.WriteLine(info)
	}
	d stream.WriteLine("----------------------------------") 
	d stream.WriteLine("")
	d stream.WriteLine("")
	d stream.SaveStream()
	d stream.%Close()
}

/// desc:获取classmethod入参值
ClassMethod GetMethodParams(className, methodName, params...)
{
	/* 查询类方法对应参数 */
    s ret = ""
	s formalSpecParsed = ..GetParamsList(className, methodName)
    q:(formalSpecParsed = "") ret
    
    /* 遍历参数 */
    for i = 1 : 1 : $ll(formalSpecParsed){
	    
		/* 参数名称 */
		s paramName = $lg($lg(formalSpecParsed, i), 1)

		/* 参数类型 */
		s paramType = $lg($lg(formalSpecParsed, i), 2)
		s str = paramName _":"_ params(i)
		/* 参数字符串以逗号分割 */
		if (i = 1){
		    s ret = str
		}else{
		    s ret = ret _ ","_ str
		}
       
    }
    q ret
}

/// desc:获取方法入参数组,无参返回空
ClassMethod GetParamsList(className, methodName)
{
    s method = className _ "||" _ methodName
    &SQL(
		SELECT FormalSpecParsed INTO :formalSpecParsed
			FROM %Dictionary.CompiledMethod
		WHERE ID1 = :method 
    )
    q $g(formalSpecParsed)
}

ClassMethod Trim(str As %String) As %String
{
	q $replace(str, $c(9), "")
}

/// 返回消息Json
ClassMethod Msg(code, msg = "", data = "") As %DynamicObject
{
    s ret = {}
    s ret.code = code
    s ret.msg = msg
    s ret.data = data
    q ret
}

/// 返回前台的成功消息
ClassMethod Success(data) As %DynamicObject
{
    q ..Msg(200, "", data)
}

/// 返回前台的成功消息转Json
ClassMethod Success2Json(data) As %String
{
    q ..Success(data).%ToJSON()
}

/// 返回前台的失败消息
ClassMethod Failure(msg, data = "") As %DynamicObject
{
    q ..Msg(-1, msg, data)
}

/// 返回前台的失败消息转Json
ClassMethod Failure2Json(msg, data = "") As %String
{
    q ..Failure(msg, data).%ToJSON()
}

/// 返回前台的警告消息
ClassMethod Warning(msg, data = "") As %DynamicObject
{
    q ..Msg(0, msg, data)
}

/// 返回前台的警告消息转Json
ClassMethod Warning2Json(msg, data = "") As %String
{
    q ..Warning(msg, data).%ToJSON()
}

}

  • M.Main
Include M.Base

Class M.Main Extends %RegisteredObject
{

/// w ##class(M.Main).MainLogic()
ClassMethod MainLogic(num = {$random(11)})
{
	s ret = ..LoginLogic(num)
	s ret = ..PayLogic(num)
	s ret = ..StockLogic(num)
	q ret
}


ClassMethod LoginLogic(num)
{
	// todo 模拟登录逻辑
	throw:(num = 1) $$$LoginException("输入密码错误")
	throw:(num = 2) $$$WarningException("账户不存在")
	throw:(num = 3) $$$LoginException("用户没有启用")
	q $$$OK
}


ClassMethod PayLogic(num)
{
	// todo 模拟支付逻辑
	throw:(num = 4) $$$PayException("余额不足")
	throw:(num = 5) $$$PayException("单笔支付过高")
	throw:(num = 6) $$$WarningException("重复支付")
	q $$$OK
}


ClassMethod StockLogic(num)
{
	// todo 模拟库存逻辑
	throw:(num = 7) $$$WarningException("库存不足")
	throw:(num = 8) $$$StockException("批次不存在")
	throw:(num = 9) $$$StockException("订单不存在")
	q $$$OK
}

}

  • M.OldHandleError
Class M.OldHandleError Extends %RegisteredObject
{

/// w ##class(M.OldHandleError).MainLogic()
ClassMethod MainLogic(num = {$random(11)})
{
	s $zt = "Error"
	s ret = ..LoginLogic(num)
	#; 需要判断分割字符串
	s code = $p(ret, "^", 1)
	s msg = $p(ret, "^", 2)
	q:(code < 0) msg
	
	s ret = ..PayLogic(num)
	#; 需要判断是否为0
	q:(ret '= 0) ret
	
	s ret = ..StockLogic(num)
	#; 需要判断是为JSON对象
	q:('$isobject(ret)) "异常错误"
	q:(ret.code <0 ) ret.msg
	
	q $$$OK
Error
	s $zt = ""
	q $ze
}

/// w ##class(M.OldHandleError).LoginLogic($random(4))
ClassMethod LoginLogic(num)
{
	// todo 模拟登录逻辑
	q:(num = 1) "-1^输入密码错误"
	q:(num = 2) "-2^账户不存在"
	q:(num = 3) "-3^用户没有启用"
	q 0
}

/// w ##class(M.OldHandleError).PayLogic($random(7))
ClassMethod PayLogic(num)
{
	// todo 模拟支付逻辑
	q:(num = 4) "余额不足"
	q:(num = 5) "单笔支付过高"
	q:(num = 6) "重复支付"
	q 0
}

/// w ##class(M.OldHandleError).StockLogic($random(9)).%ToJSON()
ClassMethod StockLogic(num)
{
	// todo 模拟库存逻辑
	q:(num = 7) {"code":-7,"msg":"库存不足"}
	q:(num = 8) {"code":-8,"msg":"批次不存在"}
	q:(num = 9) {"code":-9,"msg":"订单不存在"}
	q {"code":0,"msg":"成功"}
}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/69669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过文档改善用户体验和客户关系

文档对你的员工和客户来说都是一个宝贵的信息来源。你策划和分享的每一个教程、"如何做 "的文章和指南都可以改善用户体验和客户关系。 在这个数字时代&#xff0c;每个人都希望立即得到答案&#xff0c;很少或没有人际互动&#xff0c;一个坚实的知识库软件使你领先…

TIOBE12月编程语言榜发布:C++首超Java!

本月TIOBE更新了榜单&#xff0c;其中变化最大的莫过于C以0.12%微弱优势&#xff0c;总市场份额首次超过了Java。 在今年6月份的时候&#xff0c;TIOBE的CEOPaul Jansen曾做出过预测&#xff0c;C将会是下一个超越Java的语言。果然在最新版本的C 20的推动下&#xff0c;超越了…

模板方法模式(python)

一、模式定义 1.模板方法模式(Template Method Pattern)&#xff1a;定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 2.模板方法是一种类行为型模式。 二、模式结构 …

斩获三奖 | 国产BI行业唯一获奖,Smartbi荣获36氪、数据猿等多项荣誉

入选36氪新经济之王年度企业近日&#xff0c;36氪WISE2022 新经济之王盛会隆重举行&#xff0c;会上正式发布了“WISE2022 新经济之王年度企业”名册&#xff0c;思迈特软件成功斩获“WISE2022 新经济之王年度企业”荣誉&#xff0c;成为国产BI行业唯一获奖品牌。据了解&#x…

scrapy爬虫框架

scrapy爬虫框架一 scrapy架构介绍二 安装指南三 创建项目3.1 创建爬虫项目3.2 创建爬虫3.3 启动爬虫3.4 pycharm中运行四 scrapy解析数据五 settings相关配置六 持久化方案七 全站爬取cnblogs文章八 中间件一 scrapy架构介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为…

在线购物电商网站毕业设计,网上购物商城系统设计与实现,毕业设计怎么写论文毕设源码开题报告需求分析怎么做

项目背景和意义 目的&#xff1a;伴随着互联网技术的不断发展和完善&#xff0c;在人们的生活和工作的各个方面&#xff0c;互联网都有着非常重大的影响。伴随着国内电子商务行业的迅猛发展&#xff0c;消费者现在能够轻松的实现足不出户的&#xff0c;仅仅通过网络购物平台就可…

【工作经历分享】软件测试求职真的难,但是我还是从7K涨到了14K

上个礼拜刚好转正了&#xff0c;三个月试用期&#xff0c;五月份换的工作。 现在这份工作&#xff0c;相比上一份确实好很多&#xff0c;比如工资直接涨了一倍&#xff0c;7到14&#xff0c;13薪&#xff0c;朝九晚六&#xff0c;从不加班&#xff0c;项目也简单&#xff0c;包…

Kubernetes基础概念

什么是Kubernetes 1.用于自动部署、扩展和管理“容器化( containerized) 应用程序”的开源系统 2.可以理解成K8S是负责自动化运维管理多个容器化程序(比如Docker)的集群&#xff0c;是–个生态极其丰富的容器编排框架工具 Kubernetes作用 1.用于自动部署、扩展和管理“容器化…

2022下半年的软考成绩公布时间?

今年软考下半年成绩公布时间预估在12月15-20日左右。 软考成绩一般并没有一个固定的时间点公布&#xff0c;不过从近几年软考办成绩查询通知公布时间来看&#xff0c;大多是下午出的成绩。一般来说&#xff0c;成绩查询通知公布之前的几分钟&#xff0c;考生已经可以查成绩了。…

使用Golang语言walk框架开发一个简单的windowsGUI

1、前言 最近使用golang语言开发了一个windowsGUI的Demo&#xff0c;由于golang语言属于比较新的语言&#xff0c;对GUI开发这一块并没有原生库也没有推荐库&#xff0c;因此我在github上找到了一个叫做walk的GUI开发框架。 github原址为&#xff1a;http://github.com/lxn/w…

定时执行专家 —— 定时循环发送TCP消息(例如:控制设备的开关机等场景)

《定时执行专家》是一款制作精良、功能强大、简单易用、毫秒级精度、专业级的定时任务执行软件。软件具有 21 种【任务类型】、12 种【触发器】触发方式&#xff0c;并且全面支持界面化Cron表达式设置。软件采用多线程并发方式检测任务触发和任务执行&#xff0c;能够达到毫秒级…

带你快速入门JDBC

1&#xff0c;JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 JDBC概念 JDBC 就是使用Java语言操作关系型数据库的一套API 全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接 …

Win11的两个实用技巧系列之开机后桌面无响应怎么办?

目录 win10开机后桌面无响应怎么办?win10开机后桌面无响应点什么都不行 win10开机后桌面无响应点什么都不行怎么办&#xff1f; Win10和Win11 22H2如何关闭文字热门搜索? Win11 22H2关闭文字热门搜索 Win10 22H2关闭文字热门搜索 点击拿去 win10开机后桌面无响应怎么办…

NR PUSCH power control

这篇看下NR PUSCH power control的相关内容&#xff0c;主要内容集中在38.213 7.1章节&#xff0c;功率计算无非就是一个长公式&#xff0c;根据RRC配置的参数及后续DCI field 的内容作出功率的调整&#xff1b;最初这部分看的就云里雾里的&#xff0c;最近再看&#xff0c;相比…

环境土壤物理模型HYDRUS

HYDRUS是由著名土壤学家Rien van Genuchten和Jirka Simunek等人基于Windows系统界面开发的环境土壤物理模拟软件&#xff0c;是模拟一维和多维变饱和多孔介质的水流、溶质运移、根系吸水和溶质吸收、热量传输等的强有力工具。除基础功能以外&#xff0c;该模型还附有一系列扩展…

交叉验证、网格搜索、模型选择与调优、鸢尾花案例增加K值调优与Facebook人造世界签到位置train.csv数据预测代码实现

一、交叉验证 交叉验证(cross validation)&#xff1a;将拿到的训练数据分为训练和验证集&#xff0c;以下图为例&#xff0c;将数据分成4份&#xff0c;其中一份作为验证集&#xff0c;经过4次(组)的测试&#xff0c;每次都更换不同的验证集&#xff0c;即得到4组模型的结果&…

One-shot就能做事件抽取?ChatGPT在信息抽取上的强大应用

One-shot就能做事件抽取&#xff1f;ChatGPT在信息抽取上的强大应用0. 前言1. 灵感2. 实验3. 结论0. 前言 近期&#xff0c;OpenAI发布的chat GPT可谓是各种刷屏&#xff0c;很多人都在关注这种模式是否可以应用于搜索引擎&#xff0c;这给做搜索的朋友们带来了很大的危机感。…

强大的VS插件DevExpress CodeRush v22.1 - 让代码编程更智能

DevExpress CodeRush是一个强大的Visual Studio .NET 插件&#xff0c;它利用整合技术&#xff0c;通过促进开发者和团队效率来提升开发者体验。为Visual Studio IDE增压、消除重复的代码并提高代码质量&#xff0c;可以快速思考、自动化测试、可视化调试和重构。 CodeRush v2…

vue学习笔记(一)-vue基础语法

视频教程&#xff1a;尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili 相关文档&#xff1a;Vue核心 Vue简介 初识 (yuque.com) 兼容性 Vue 不支持 IE8 及以下版本&#xff0c;因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAS…

RabbitMQ入门

1. 什么是MQ 消息队列(Message Queue&#xff0c;简称MQ)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO先入先出&#xff0c;只不过队列中存放的内容是message而已 作用&#xff1a;应用程序“对”应用程序的通信方法。 2. 应用场景 主要解决异步处理…