浏览代码

!10786 身份证读证流程优化
Merge pull request !10786 from 80310970/IDCerOptimize_0605

刘文涛80174520 3 月之前
父节点
当前提交
a519362165

+ 2 - 1
DevAdapter/include/DevErrorCode.h

@@ -1136,7 +1136,7 @@ typedef short DECRESULT;
 #define MEC_DEVAPI_IDCER_(funcName)	( MEC_DEVAPI_IDCER_##funcName )
 #define MEC_DEVAPI_IDCER_DevOpen				     (DEC_NO_E_IDCERTIFICATE_START + 1) //20100001
 #define MEC_DEVAPI_IDCER_IDCerRFControl				 (DEC_NO_E_IDCERTIFICATE_START + 2) //20100002
-#define MEC_DEVAPI_IDCER_IDCerAuthenticate			 (DEC_NO_E_IDCERTIFICATE_START + 3) //20100003
+#define MEC_DEVAPI_IDCER_IDCerAuthenticate_Error	 (DEC_NO_E_IDCERTIFICATE_START + 3) //20100003
 #define MEC_DEVAPI_IDCER_IDCerAuthenticate_NotIDCard (DEC_NO_E_IDCERTIFICATE_START + 4) //20100004
 #define MEC_DEVAPI_IDCER_IDCerGetDataEx2			 (DEC_NO_E_IDCERTIFICATE_START + 5) //20100005
 #define MEC_DEVAPI_IDCER_ForceIDEject				 (DEC_NO_E_IDCERTIFICATE_START + 6) //20100006
@@ -1145,6 +1145,7 @@ typedef short DECRESULT;
 #define MEC_DEVAPI_IDCER_GetDevCategory              (DEC_NO_E_IDCERTIFICATE_START + 9) //20100009
 #define MEC_DEVAPI_IDCER_Reset                       (DEC_NO_E_IDCERTIFICATE_START + 10) //2010000A
 #define MEC_DEVAPI_IDCER_DevClose                    (DEC_NO_E_IDCERTIFICATE_START + 11) //2010000B
+#define MEC_DEVAPI_IDCER_IDCerAuthenticate_NoCard    (DEC_NO_E_IDCERTIFICATE_START + 12) //2010000C
 
 
 #define DEC_NO_E_IDCERTIFICATE (DEC_NO_E_IDCERTIFICATE_START + CAPACITY_FOR_MEC_DEVAPI)

文件差异内容过多而无法显示
+ 100 - 669
Module/mod_IDCertificate/IDCertFSM.cpp


+ 58 - 51
Module/mod_IDCertificate/IDCertFSM.h

@@ -76,7 +76,6 @@ enum EvtType
 	USER_EVT_EXIT,
 	USER_EVT_GET_DEVINFO,
 	USER_EVT_ERROR,
-	USER_EVT_WAIT_FETCH_IDCARD_FINISHED,
 	USER_EVT_INIT_FINISHED,
 	USER_EVT_TODO_INIT,
 	USER_EVT_TODO_INIT_FINISHED,
@@ -115,6 +114,26 @@ struct CtxInfo
 	LPVOID pCtx;
 };
 
+struct IDCerTextData
+{
+	unsigned char name[1024];
+	unsigned char sex[1024]; 
+	unsigned char nation[1024]; 
+	unsigned char birthday[1024];
+	unsigned char address[1024]; 
+	unsigned char idno[1024];
+	unsigned char department[1024];
+	unsigned char startDate[1024]; 
+	unsigned char endDate[1024]; 
+	unsigned char englishName[1024];
+	unsigned char nationality[1024];
+	unsigned char idVersion[1024];
+	unsigned char idType[1024];
+	unsigned char reserved[1024];
+	unsigned char englishNameEx[1024]; //英文名备用字段
+	unsigned char IssuedSN[1024]; //换证次数
+};
+
 class CancelReadEvent : public FSMEvent
 {
 public:
@@ -167,46 +186,38 @@ public:
 class CIDCertFSM : public CCommDevFSM<CIDCertFSM, IDCerClass>
 {
 public:
-	enum {s0,s1,s2,s3,s4,s5};
+	enum {s0,s1,s2,s3};
 
 	BEGIN_FSM_STATE(CIDCertFSM)
 		FSM_STATE_ENTRY(s0,"Normal",s0_on_entry,s0_on_exit,s0_on_event)
 		FSM_STATE_ENTRY(s1,"Reading",s1_on_entry,s1_on_exit,s1_on_event)
 		FSM_STATE_ENTRY(s2,"Fail",s2_on_entry,s2_on_exit,s2_on_event)
-		FSM_STATE_ENTRY(s3, "Eject", s3_on_entry, s3_on_exit, s3_on_event)
-		FSM_STATE_ENTRY(s4, "WaitingFetch", s4_on_entry, s4_on_exit, s4_on_event)
-		FSM_STATE_ENTRY(s5, "Init", s5_on_entry, s5_on_exit, s5_on_event)
+		FSM_STATE_ENTRY(s3, "Init", s3_on_entry, s3_on_exit, s3_on_event)
 	END_FSM_STATE()
 
-	BEGIN_FSM_RULE(CIDCertFSM, s5)
+	BEGIN_FSM_RULE(CIDCertFSM, s3)
 		FSM_RULE_ENTRY(s0, s2, USER_EVT_ERROR, 0)
 		FSM_RULE_ENTRY(s0, FSM_STATE_EXIT, USER_EVT_QUIT, 0)
 		FSM_RULE_ENTRY(s0, s1, USER_EVT_READ_AND_SCAN_UTF8, 0) //ex4
 		FSM_RULE_ENTRY(s0, s1, USER_EVT_READ_AND_SCAN_UTF8JS, 0) //ex4
-		FSM_RULE_ENTRY(s0, s5, USER_EVT_TODO_INIT_FINISHED, 0)
+		FSM_RULE_ENTRY(s0, s3, USER_EVT_TODO_INIT_FINISHED, 0)
 		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 0)
-		FSM_RULE_ENTRY(s1, s2, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 1)
+		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 1)
 		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 2)
-		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 3)
-		FSM_RULE_ENTRY(s1, s4, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 4)
-		FSM_RULE_ENTRY(s1, s5, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 5)
+		FSM_RULE_ENTRY(s1, s3, USER_EVT_READ_AND_SCAN_UTF8_FINISHED, 3)
 		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 0)
-		FSM_RULE_ENTRY(s1, s2, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 1)
-		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 2)
-		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 3)
-		FSM_RULE_ENTRY(s1, s4, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 4)
-		FSM_RULE_ENTRY(s1, s5, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 5)
+		FSM_RULE_ENTRY(s1, s0, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 1)
+		FSM_RULE_ENTRY(s1, s3, USER_EVT_READ_AND_SCAN_UTF8JS_FINISHED, 3)
 		FSM_RULE_ENTRY(s1, FSM_STATE_EXIT, USER_EVT_QUIT, 0)
 		FSM_RULE_ENTRY(s1, s0, USER_EVT_CANCEL_READ, 2)
 		FSM_RULE_ENTRY(s1, s0, USER_EVT_EXIT, 3)
-		FSM_RULE_ENTRY(s2, s5, USER_EVT_TODO_INIT_FINISHED, 0)
-		FSM_RULE_ENTRY(s4, s0, USER_EVT_WAIT_FETCH_IDCARD_FINISHED, 0)
-		FSM_RULE_ENTRY(s5, s0, USER_EVT_INIT_FINISHED, 0)
-		FSM_RULE_ENTRY(s5, s2, USER_EVT_INIT_FINISHED, 2)
+		FSM_RULE_ENTRY(s2, s3, USER_EVT_TODO_INIT_FINISHED, 0)
+		FSM_RULE_ENTRY(s3, s0, USER_EVT_INIT_FINISHED, 0)
+		FSM_RULE_ENTRY(s3, s2, USER_EVT_INIT_FINISHED, 2)
 	END_FSM_RULE()
 
-		CIDCertFSM() :m_bCancelRead(false), m_bReading(false), m_bWaitReadMore(false),
-		m_bExit(false), m_testResult(Error_Succeed), m_getDevCategory(Error_Unexpect), m_csMachineType(""), m_terminalNo(""), invalidBreak(false), transImgMsg("")
+		CIDCertFSM() :m_bCancelRead(false), m_bReading(false),
+		m_bExit(false), m_testResult(Error_Succeed), m_getDevCategory(Error_Unexpect), m_csMachineType(""), m_terminalNo(""), transImgMsg("")
 	{
 		ZeroMemory(&m_adapterInfo, sizeof(m_adapterInfo));
 		HARDWARE_ENTITY_RESET_ENTITYID(m_entCode, 0x201);
@@ -231,18 +242,10 @@ public:
 	virtual void s3_on_entry();
 	virtual void s3_on_exit();
 	virtual unsigned int s3_on_event(FSMEvent* e);
-	virtual void s4_on_entry();
-	virtual void s4_on_exit();
-	virtual unsigned int s4_on_event(FSMEvent* e);
-	virtual void s5_on_entry();
-	virtual void s5_on_exit();
-	virtual unsigned int s5_on_event(FSMEvent* e);
 
 	int ReadAndScanUTF8(SpReqAnsContext<IDCert_ReadAndScanUTF8_Req, IDCert_ReadAndScanUTF8_Ans>::Pointer ctx); 
 	int ReadAndScanUTF8JS(SpReqAnsContext<IDCert_ReadAndScanUTF8JS_Req, IDCert_ReadAndScanUTF8JS_Ans>::Pointer ctx);
 
-	int WaitFetchIDCard();
-	void SetReadMore(){m_bWaitReadMore = true;}
 	void SetExitFlag(){m_bExit = true;}
 	bool GetReadFlag(){return m_bReading;}
 	ErrorCodeEnum GetDevCatInfo(DevCategoryInfo &devInfo, CSimpleStringA& devType);
@@ -265,21 +268,30 @@ private:
 	//Delete bmp file in dep directory, you should just convey fileName only without paths -Joseph
 	//deleteTiming:0, defalut; deleteTiming:1, delete before Read IDCard; deleteTiming:2, delete after Read IDCard
 	ErrorCodeEnum DeleteFileIfExisted(LPCTSTR fileName, int deleteTiming = 0);
-	BOOL UCS2_to_UTF8(UINT16* ucs2_code, UINT8* utf8_code);
-	BOOL GetSexUTF8String(UINT16* in, UINT8* out);
-	BOOL GetNationalUTF8String(UINT16* in, UINT8* out);
-	BOOL GetDateStandardFormatUTF8(UINT16* in, UINT8* out);
-	BOOL RemoveUCS2Blank(UINT16* ucs2_code);
+	void UCS2_to_UTF8(UINT16* ucs2_code, UINT8* utf8_code);
+	void GetSexUTF8String(UINT16* in, UINT8* out);
+	void GetNationalUTF8String(UINT16* in, UINT8* out);
+	void GetDateStandardFormatUTF8(UINT16* in, UINT8* out);
+	void RemoveUCS2Blank(UINT16* ucs2_code);
 	int GetUCS2ByteLength(UINT16* ucs2_code);
 	void CheckHanZi(UINT16* ucs2_code);
 
 	CSimpleStringA GetFileHashStr(CSimpleStringA filePath);
 	CSimpleStringA GetFileLastModifyTime(CSimpleStringA filePath);
 
+	template <typename T>
+	void CopyIDCerDataToCtx(IDCerInfoEx2 idInfoEx2, T& ctx); //兼容JS接口
+	BOOL GetScanImg(IDCerInfoEx2 idInfoEx2, CBlob& frontImg, CBlob& backImg);
+#ifdef RVC_OS_WIN
+	char* GetGBKString(UINT16* ucs2_code); //WIN打印源数据日志或其他中文用途
+#endif
+	//记录身份证相关图片的最近修改时间,用于上送信息调研,后续下掉 - 2025.6.13 CJL
+	void WarnImgCreateTime();
+
 public:
 		std::string checkImgURL;
 private:
-	bool m_bCancelRead, m_bReading, m_bWaitReadMore, m_bExit;
+	bool m_bCancelRead, m_bReading, m_bExit;
 	ErrorCodeEnum m_testResult;
 	ErrorCodeEnum m_getDevCategory;
 	DevCategoryInfo m_devCatInfo;
@@ -293,12 +305,17 @@ private:
 	BOOL supportUCS2; //支持生僻字
 	BOOL igestionVer; //吸入式设备
 	BOOL supportNewForeigner; //支持新版外国人永居证 2023.11.10
-	CSimpleStringA bkPicPath; //合成背景的图片路径
-	
 
 	BOOL closeImgCheck;
-	bool invalidBreak; //TODO: 只有赋值,没有调用,考虑移除  [Gifur@2025228]
 	ULLINT m_ullBeginTime, m_ullEndTime;
+	ULLINT OpenRFControlTime;
+	ULLINT CloseRFControlTime;
+	ULLINT IDCerAuthenticateTime;
+	ULLINT IDCerGetDataEx2Time;
+	ULLINT ScanIDAndSaveImageTime;
+	//记录身份证相关图片的最近修改时间,用于上送信息调研,后续下掉 - 2025.6.13 CJL
+	CSimpleStringA headPhotoTimeStr, frontPhotoTimeStr, backPhotoTimeStr;
+	time_t headPhotoTime, frontPhotoTime, backPhotoTime, currentSysTime;
 };
 
 struct ReadAndScanUTF8Task : public ITaskSp  //ex8
@@ -331,18 +348,6 @@ struct ReadAndScanUTF8JSTask : public ITaskSp  //ex8
 	}
 };
 
-struct WaitFetchIDCardTask : public ITaskSp
-{
-	CIDCertFSM* fsm;
-	WaitFetchIDCardTask(CIDCertFSM* f) : fsm(f) {}
-
-	void Process()
-	{
-		FSMEvent *e = new FSMEvent(USER_EVT_WAIT_FETCH_IDCARD_FINISHED);
-		e->param1 = fsm->WaitFetchIDCard();
-		fsm->PostEventFIFO(e);
-	}
-};
 struct InitTask : public ITaskSp
 {
 	CIDCertFSM* fsm;
@@ -355,4 +360,6 @@ struct InitTask : public ITaskSp
 		fsm->PostEventFIFO(e);
 	}
 };
+
+
 #endif //IDCERTFSM_H

+ 37 - 11
Module/mod_IDCertificate/IDCertificate.xml

@@ -1,18 +1,22 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <entity name="IDCertificate">
-	<class name="IDCertService" overlap="true" exclusive="false">			
-		<oneway name="CancelRead" overlap="true" method_id="1">		
-		</oneway>		
-		<!-- 1、设备未打开,errorCodeΪError_DevNotAvailable(2050),rtaCodeΪRTA2105 -->
-		<twoway name="GetDevInfo" overlap="true" method_id="65535">
+	<class name="IDCertService" overlap="true" exclusive="false">
+		<oneway name="CancelRead" overlap="true" method_id="1">
+		</oneway>
+		<!-- 1、设备未打开,errorCode为Error_DevNotAvailable(2050),rtaCode为RTA2105 -->
+		<twoway name="GetDevInfo" overlap="true" jsflag="true" method_id="65535">
 			<req>
 			</req>
 			<res>
+				<!--设备适配器DevCategoryInfo的szType-->
 				<param name="type" type="string" />
+				<!--设备适配器DevCategoryInfo的szModel-->
 				<param name="model" type="string" />
+				<!--设备适配器DevCategoryInfo的version-->
 				<param name="version" type="string" />
+				<!--硬件实体内置的状态,目前暂无使用场景,有需要再讨论如何定义-->
 				<param name="state" type="int" />
-			</res>			
+			</res>
 		</twoway>
 		<twoway name="ReadAndScanUTF8" overlap="true" method_id="9">
 			<req>
@@ -64,33 +68,55 @@
 				<param name="reserved2" type="array_string"/>
 			</req>
 			<res>
-				<!--msgtype用于区分传递方式的新旧-->
-				<param name="msgtype" type="int" />
+				<!--是否扫描成功-->
 				<param name="hasscan" type="int" />
-				<param name="photodata" type="blob" />
+				<!--正面扫描件-->
 				<param name="frontphoto" type="blob" />
+				<!--背面扫描件-->
 				<param name="backphoto" type="blob" />
+				<!--头像图片-->
 				<param name="headphoto" type="blob" />
+				<!--指纹数据1-->
 				<param name="finger1" type="blob" />
+				<!--指纹数据2-->
 				<param name="finger2" type="blob" />
+				<!--图片预留字段1-->
 				<param name="imgreserved1" type="blob" />
+				<!--图片预留字段2-->
 				<param name="imgreserved2" type="blob" />
+				<!--预留字段1-->
 				<param name="reserved1" type="array_int"/>
+				<!--预留字段2-->
 				<param name="reserved2" type="array_string"/>
+				<!--姓名-->
 				<param name="name_utf8" type="blob" />
+				<!--性别-->
 				<param name="sex_utf8" type="blob" />
+				<!--民族-->
 				<param name="nation_utf8" type="blob" />
+				<!--生日-->
 				<param name="birthday_utf8" type="blob" />
+				<!--地址-->
 				<param name="address_utf8" type="blob" />
+				<!--证件号码-->
 				<param name="idcode_utf8" type="blob" />
+				<!--签发机关-->
 				<param name="department_utf8" type="blob" />
+				<!--发证日期-->
 				<param name="startdate_utf8" type="blob" />
+				<!--证件有效期截止日期-->
 				<param name="enddate_utf8" type="blob" />
+				<!--英文名-->
 				<param name="englishname_utf8" type="blob" />
+				<!--国家-->
 				<param name="nationality_utf8" type="blob" />
+				<!--证件版本-->
 				<param name="idversion_utf8" type="blob" />
+				<!--证件类型-->
 				<param name="idtype_utf8" type="blob" />
+				<!--备用字段-->
 				<param name="othercode_utf8" type="blob" />
+				<!--备用字段-->
 				<param name="reserved_utf8" type="blob" />
 			</res>
 		</twoway>
@@ -105,6 +131,6 @@
 		</twoway>
 	</class>
 	<message name="FetchIDCard">
-			<param name="status" type="int"/>
-	</message>	
+		<param name="status" type="int"/>
+	</message>
 </entity>

+ 1 - 0
Module/mod_IDCertificate/IDCertificate_LogCode.h

@@ -6,6 +6,7 @@ namespace IDCertificate {
 
 #define IDCertService_LogCode_OpenIDCerRFControl "QLR040220121"
 #define IDCertService_LogCode_CloseIDCerRFControl "QLR040220122"
+#define IDCertService_LogCode_IDCerGetDataEx2 "QLR040220123"
 
 }
 #endif

+ 3 - 1
Module/mod_IDCertificate/IDCertificate_UserErrorCode.h

@@ -25,7 +25,9 @@
 #define IDCertificate_UserErrorCode_ReadAndScan_NotOnReading	0x2010021f //实体不在读证状态
 #define IDCertificate_UserErrorCode_FindFile_in_DepBak			0x20100220 //旧dep目录下获取到厂商文件
 #define IDCertificate_UserErrorCode_Timeout_NoCard			0x20100221 //超时未插卡
-#define IDCertificate_UserErrorCode_Timeout_OtherCard		0x20100221 //插入其他卡片(非身份证)
+#define IDCertificate_UserErrorCode_Timeout_OtherCard		0x20100222 //插入其他卡片(非身份证)
+
+#define IDCertificate_UserErrorCode_GetImgFileTime			0x20100230 //图片文件的最近修改时间
 
 
 //#define IDCertificate_UserErrorCode_Real_Root_Config		(IDCertificate_UserErrorCode_Start + 31) //加载实际的root配置

+ 1 - 3
Module/mod_IDCertificate/IDCertificate_def_g.h

@@ -127,9 +127,7 @@ struct IDCertService_ReadAndScanUTF8JS_Req
 
 struct IDCertService_ReadAndScanUTF8JS_Ans
 {
-	int msgtype;
 	int hasscan;
-	CBlob photodata;
 	CBlob frontphoto;
 	CBlob backphoto;
 	CBlob headphoto;
@@ -157,7 +155,7 @@ struct IDCertService_ReadAndScanUTF8JS_Ans
 
 	void Serialize(SpBuffer &Buf)
 	{
-		auto & buf = Buf & msgtype & hasscan & photodata & frontphoto & backphoto & headphoto & finger1 & finger2 & imgreserved1 & imgreserved2 & reserved1 & reserved2 & name_utf8 & sex_utf8 & nation_utf8 & birthday_utf8 & address_utf8 & idcode_utf8 & department_utf8 & startdate_utf8 & enddate_utf8 & englishname_utf8 & nationality_utf8 & idversion_utf8 & idtype_utf8 & othercode_utf8 & reserved_utf8;
+		auto & buf = Buf & hasscan & frontphoto & backphoto & headphoto & finger1 & finger2 & imgreserved1 & imgreserved2 & reserved1 & reserved2 & name_utf8 & sex_utf8 & nation_utf8 & birthday_utf8 & address_utf8 & idcode_utf8 & department_utf8 & startdate_utf8 & enddate_utf8 & englishname_utf8 & nationality_utf8 & idversion_utf8 & idtype_utf8 & othercode_utf8 & reserved_utf8;
 	}
 
 };

+ 3 - 3
Module/mod_accessauth/AccessAuthFSM.cpp

@@ -656,7 +656,7 @@ void CAccessAuthFSM::s3_on_entry()
 {
 	LOG_FUNCTION();
 	CSystemStaticInfo si;
-	DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setLogCode(AccessAuthService_LogCode_Regist)("终端准入成功");
+	DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode(AccessAuthService_LogCode_Regist)("终端准入成功");
 
 	m_pEntity->GetFunction()->GetSystemStaticInfo(si);
 	if (si.InstallVersion.ToString().IsNullOrEmpty()) {
@@ -1012,7 +1012,7 @@ void CAccessAuthFSM::AuthLogWarn(const T& ret, const string& url, const string&
 			msg = CSimpleStringA::Format("%s失败,请尝试重启应用", method.c_str());
 		}
 		doWarnMsg(acsErrCode, msg.GetData(), bNeedEvent);
-		DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setLogCode(AccessAuthService_LogCode_Regist)(msg.GetData());
+		DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_USER).setLogCode(AccessAuthService_LogCode_Regist)(msg.GetData());
 	}
 	else {
 		SP::Module::Restful::CommResponseJson responseStatus;
@@ -1020,7 +1020,7 @@ void CAccessAuthFSM::AuthLogWarn(const T& ret, const string& url, const string&
 		msg = CSimpleStringA::Format("{\"errcode\": \"%s\", \"message\": %s}",
 			responseStatus.errorCode.c_str(), responseStatus.errorMsg.c_str());
 		doWarnMsg(ERR_ACCESSAUTH_SERVICE_FAILED, msg.GetData(), bNeedEvent);
-		DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM).setResultCode("RTA520A").setLogCode(AccessAuthService_LogCode_Regist)
+		DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_USER).setResultCode("RTA520A").setLogCode(AccessAuthService_LogCode_Regist)
 			(msg.GetData());
 	}
 }

部分文件因为文件数量过多而无法显示