版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第十二章补充具体程序main.cpp/ 因为DBS是运行于linux环境下的,所以main函数是console的形式CGWSMgr g_GWSMgr; / 定义一个全局GameWorldServer管理类实例int main(int argc, char* argv) LOG(LV_M, "DBServer Start .");if(!g_GWSMgr.Init() / 执行CGWSMgr的Init函数来初始化有关数据,建立监听socket等return 0;int bRun = 1;while(bRun) / 执行CGWSMgr的Run函数来进行DBS的主体循环处理 bR
2、un = g_GWSMgr.Run();LOG(LV_M , "DBServer Exit !");return 0;gws.h/ 下面定义游戏用到的数据表名static const char CHARACTER_TB = "cha_tb" / 角色表static const char ITEM_TB = "item_tb" / 道具表static const char MAIL_TB = "mail_tb" / 邮件表/ DBS自身基于来自Packet消息的调用, 这里的packet只是数据存放使用,并不一定来自
3、网络消息/之所以这样存放,是为了解析此类消息的时候,与解析网络消息的形式以及格式保持一致enum DBS_MSG _DBS_MSG_BASE = 5000, DBS_FACTION_CAL_EXP / 计算帮派总经验值的消息,是定时触发的;/ 代表单个GWS的类CGWSclass CGWSprivate: int _nID; / 预先分配好的ID, 在GWS登录DBS以后会赋值 int _nState; map<int , CGamePlayer*> _PlayerIdx; / 玩家ID到数据指针的映射,方便查找public:CGWS();CGWS();CMFTCPSession
4、*m_Link; / 用来保持和GWS连接的socket/ 下面是一些对Player数据的操作函数void SetID(int nID); int GetID(); void SetState(int nState); void AddPlayer(CGamePlayer *pPlayer); bool RemovePlayer(int nID); void ClearPlayerList(); CGamePlayer* CheckPlayerOnline(int nID); CGamePlayer* GetPlayer(int nID);/ 处理来自GWS的命令的统一的函数定义, 参数固定为
5、2个, 1是GWS的指针, 标识消息来自于谁/ 2是数据包的packet指针,内含数据实际内容, 对于数据库服务器这样的应用程序,很适合使用类/ 似的函数定义方法来统一处理消息, 可以使代码结构清晰, 功能明确, 还可以方便的统计函数的/ 执行时间以评估对数据库操作的性能typedef void (*GWS2DBSPROC)(CGWS *pGWS, CMFCmdPacket *pPacket);/ 管理所有连接上DBS的GWS的类CGWSMgr/ 也是最大,最关键的一个类,它控制了整个程序的运行流程/ 此类里面包含了很多static的变量和函数, 主要是基于用消息触发回调函数的需要, / 因为
6、回调函数只能是全局的,如果想包含在类里面,就必须是static类型,它能存取/ 的变量也必须是static类型class CGWSMgrprivate: static CLog* _pL; static CDBMySql _DB; / 这是负责存取数据库的一个MySql接口类的实例 / 下面定义一些字符串数组, 作为生成以及存放sql语句字段,条件等等的公用内存static string _strSQL; static char _szSQL32768; static char _szCon256; static char _szField32; static char _szBuf4096;
7、 static list<CGWS*> _GWSList; / 所有连接上来的GWS的列表 list<CGWS*> _DeadList; / 记录当前失去连接的GWS static CMFCmdPacket _SendCmd; / 发送消息时使用的一个公有packet数据包内存 CMFCmdPacket _RecvCmd; / 接受消息时使用的一个公有packet数据包内存 int _nRunCnt; / DBServer循环计数/ 消息处理函数, 所有的网络消息以及DBS内部消息都调用它来处理void _HandleCmd(CGWS *pGWS, CMFCmdPack
8、et *pCmd);struct SProcess / 消息处理函数信息, 记录函数指针,标识函数的字符串名字,以及函数执行时间的记录 char szName24; / 名字 int nTimeCnt; / 耗时累计 GWS2DBSPROC pfn; / 函数 ; map<short, SProcess> _ProcessList; / 从消息类型定义到处理函数的指针的映射/ 添加一个新的消息处理函数 void _AddProcess(short sMsgType, GWS2DBSPROC pfn , char *pszName = NULL); / 下面列出的就是由消息触发的回调
9、函数指针, 它们的格式是完全统一的, / 由函数的名字也可以大致知道它的作用, 后面我们会列出部分很典型的函数内容来介绍 static void _HandleCmd_GWSLogin(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_CreateCha(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_CreatePet(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_SaveCha(CGWS *pG
10、WS , CMFCmdPacket *pCmd); static void _HandleCmd_QueryActCha(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_QueryCha(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_DeleteCha(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_UserEnter(CGWS *pGWS , CMFCmdPacket*pCmd); static
11、 void _HandleCmd_UserLeave(CGWS *pGWS , CMFCmdPacket*pCmd); static void _HandleCmd_CreateFaction(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_DeleteFaction(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_QueryFactionList(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_U
12、pdateFaction(CGWS *pGWS, CMFCmdPacket *pCmd); static void _HandleCmd_FactionApply(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_ChangeFactionLeader(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_QueryFactionMemberList(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_Quer
13、yFactionByID(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_FactionAccept(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_FactionLeave(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_FactionQuit(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_FactionCalExp(CGWS *
14、pGWS, CMFCmdPacket *pCmd); static void _HandleCmd_CreateItem(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_DeleteItem(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_CreateMail(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_DeleteMail(CGWS *pGWS , CMFCmdPacket *pCmd); s
15、tatic void _HandleCmd_QueryMail(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_RefreshFriendList(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_GWSProxy(CGWS *pGWS , CMFCmdPacket *pCmd); static void _HandleCmd_SQLProxy(CGWS *pGWS, CMFCmdPacket *pCmd); / 公用的发送packet函数 static void
16、_SendMsg(CMFCmdPacket *pCmd , CGWS *pGWS = NULL , bool bExcept = false, bool bEndWrite = true); static void _NewPacket(short sMsgType); / packet操作实用函数,用指定的消息开始一个packet / 下面有一些封装和简化SQL操作的实用函数static bool _RunSQL(const char *pszSQL, bool bShow = false); / 执行指定SQL语句的函数/ 根据指定条件, 对指定数据表查询并返回符合条件的记录数量stati
17、c long _SQLGetCount(const char *pszTable, const char *pszCon); / 针对帮派这种跨多个GWS的游戏内容的处理static int _nFactionCheckTick; / 检查帮派数据的时间计数 static float _fFactionCheckInterval; / 时间间隔 static void _CheckFactionValid(); / 检查已经无效的帮派并作相应处理 static int _nCalFactionExpTick; / 帮派总经验值计算的时间计数 static float _fCalFactionE
18、xpInterval; / 时间间隔 public: / 有关数据库备份的处理static int m_nBackupHour; / 备份时间static int m_nBackupMin; static void DoBackup(int nWeekday);/ 执行备份操作static void CheckBackup(); / 检查时间public:CGWSMgr();CGWSMgr();CMFNetSocket m_ListenSock; / 用来监听的socketbool Init(); / 初试化int Run(); / 主体循环bool AddGWS(CGWS *pGWS); /
19、 添加新的GWS/ 下面是数据包packet解析和sql语句之间的转换函数 / 把一个packet包含的数据转化为一条SQL insert语句static void Util_Packet2SQL_Insert(CMFCmdPacket *pCmd, string &strSQL, char *pszField = NULL,char *pszValue = NULL); / 把一个packet包含的数据转化为一条SQL update语句static void Util_Packet2SQL_Update(CMFCmdPacket *pCmd, string &strCmd,
20、char *pszCon, const char* pszTab);/ 把一个字段的名字,数据类型,数据值写入指定的packetstatic void Util_Field2Packet(char *pszName, int nType, char *pszValue, CMFCmdPacket *pCmd); / 从packet里读出一个字段信息static void Util_GetFieldValue(CMFCmdPacket *pCmd, string &strValue); / 写入当前的字段列表(字段名字)到packet static void Util_FieldList
21、2Packet(int nFieldCnt, CMFCmdPacket *pCmd); / 读出当前数据表的记录,写入packetstatic void Util_SQLData2Packet(CMFCmdPacket *pCmd);inline void CGWSMgr:_NewPacket(short sMsgType) _SendCmd.BeginWrite(); _SendCmd.WriteShort(sMsgType);/ 添加消息处理函数inline void CGWSMgr:_AddProcess(short sMsgType, GWS2DBSPROC pfn, char *ps
22、zName) SProcess p; p.nTimeCnt = 0; p.pfn = pfn; if(pszName=NULL) strcpy(p.szName , ""); else strcpy(p.szName , pszName); _ProcessListsMsgType = p;/ 建立从消息类型到处理函数指针的映射gws.cpp#ifdef LINUXvoid pfnBackupRoutine(int nSig) / 在linux下,用一个系统定时信号来触发数据库备份操作 if(nSig=SIGALRM) / printf("Receive Back
23、up Signal %dn", nSig); g_GWSMgr.CheckBackup(); #endif/ 下面是class CGWS的实现CGWS:CGWS() _nID = 0; m_Link = NULL;CGWS:CGWS() if(m_Link) delete m_Link;/ 添加新的玩家到GWS的列表void CGWS:AddPlayer(CGamePlayer *pPlayer)_PlayerIdxpPlayer->Info.nChaID = pPlayer; / 建立从玩家ID到玩家数据的映射, 在DBS里,/ 所需要玩家的数据实际上很少,仅仅是角色ID,
24、账号ID,是否在线等简单的几项数据而已/ 从GWS的玩家列表中删掉bool CGWS:RemovePlayer(int nID) map<int, CGamePlayer*>:iterator it = _PlayerIdx.find(nID); if(it!=_PlayerIdx.end() CGamePlayer *pPlayer = (*it).second; delete pPlayer; _PlayerIdx.erase(it); return true; return false;/ 检查某个ID的玩家是否在线CGamePlayer* CGWS:CheckPlayerO
25、nline(int nID) return GetPlayer(nID);/ 由ID取得玩家数据CGamePlayer* CGWS:GetPlayer(int nID) map<int, CGamePlayer*>:iterator it = _PlayerIdx.find(nID); if(it!=_PlayerIdx.end() return (*it).second; return NULL;/ 清空玩家列表void CGWS:ClearPlayerList() for(map<int, CGamePlayer*>:iterator it = _PlayerIdx
26、.begin(); it!=_PlayerIdx.end(); it+) CGamePlayer *pPlayer = (*it).second; delete pPlayer; _PlayerIdx.clear();/ 下面是class CGWSMgr的实现CGWSMgr:CGWSMgr() _nFactionCheckTick = 0; _fFactionCheckInterval = 60.0f * 6.0f; / 以分钟为单位, 6个小时检查一次 _nCalFactionExpTick = 0; _fCalFactionExpInterval= 30.0f; / 每30分钟计算一次 m
27、_nBackupHour = 6; m_nBackupMin = 0;/ DBS初试化操作, 读取配置文件, 建立数据库连接, 开始监听等等bool CGWSMgr:Init() string strLogName = Util_CurTime2String(1); _pL = g_LogMgr.Add(strLogName.c_str(); / 读取配置文件, 取得梆定IP和端口等运行DBSA所需的配置信息 if(!_TextLoader.OpenFile("config.txt") LOG(LV_H, "Open Config Txt Failed! , Ex
28、it!"); return false; _pL->L("load config : okn"); const char *pszDBIP = _TextLoader.GetNode("db_host_ip"); const char *pszBindIP = _TextLoader.GetNode("db_bind_ip"); const char *pszDBPort = _TextLoader.GetNode("db_host_port"); const char *pszDBName = _
29、TextLoader.GetNode("db_name"); const char *pszUser = _TextLoader.GetNode("db_username"); const char *pszPass = _TextLoader.GetNode("db_password"); const char *pszPort = _TextLoader.GetNode("listen_port"); / 与数据库建立连接 if(!_DB.Connect(pszDBIP, atoi(pszDBPort), ps
30、zUser, pszPass, pszDBName) LOG(LV_H , "ERR : Connect DB Failed!"); return false; LOG(LV_M , "Connect DB Successful!"); _pL->L("connect db : okn"); / 建立监听端口if(!m_ListenSock.Initialize(PROTOCOL_TCP, (char*)pszBindIP, atoi(pszPort),true) LOG(LV_H , "ListenSock Init
31、 Failed!"); return false; m_ListenSock.Listen(); char szLocalIP255; unsigned long dwIP; char cMark; m_ListenSock.GetLocalAddr(szLocalIP , &dwIP , &cMark); _pL->L("Begin Listening . , IP = %s , Port = %dn", szLocalIP, 7812); / 下面就是建立各类消息与其对应的处理函数的联系, 并赋与一个字符串名字,以方便调试和察看_AddP
32、rocess(GWS2DBS_PROXY_PACKET , _HandleCmd_GWSProxy , "GWS_PROXY"); _AddProcess(GWS2DBS_PROXY_SQL , _HandleCmd_SQLProxy , "SQL_PROXY"); _AddProcess(GWS2DBS_LOGIN , _HandleCmd_GWSLogin , "GWS_LOGIN"); _AddProcess(GWS2DBS_CREATE_CHAR , _HandleCmd_CreateCha , "CREATE_CH
33、A"); _AddProcess(GWS2DBS_QUERY_ACT_CHAR , _HandleCmd_QueryActCha , "QUERY_ACT_CHA"); _AddProcess(GWS2DBS_QUERY_CHAR , _HandleCmd_QueryCha , "QUERY_CHA"); _AddProcess(GWS2DBS_DELETE_CHAR , _HandleCmd_DeleteCha , "DELETE_CHA"); _AddProcess(GWS2DBS_CREATE_ITEM , _Hand
34、leCmd_CreateItem , "CREATE_ITEM"); _AddProcess(GWS2DBS_SAVE_CHAR , _HandleCmd_SaveCha , "SAVE_CHA"); _AddProcess(GWS2DBS_USER_ENTER , _HandleCmd_UserEnter , "USER_ENTER"); _AddProcess(GWS2DBS_USER_LEAVE , _HandleCmd_UserLeave , "USER_LEAVE"); _AddProcess(GWS2D
35、BS_FACTION_CREATE , _HandleCmd_CreateFaction , "FA_CREATE"); _AddProcess(GWS2DBS_FACTION_GET_FACTION_LIST, _HandleCmd_QueryFactionList , "FA_QUERY_LIST"); _AddProcess(GWS2DBS_FACTION_DISMISS , _HandleCmd_DeleteFaction , "FA_DELETE"); _AddProcess(GWS2DBS_FACTION_UPDATE ,
36、 _HandleCmd_UpdateFaction , "FA_UPDATE"); _AddProcess(GWS2DBS_FACTION_CHANGE_LEADER , _HandleCmd_ChangeFactionLeader , "FA_CHANGE_LEADER"); _AddProcess(GWS2DBS_FACTION_APPLY , _HandleCmd_FactionApply , "FA_APPLY"); _AddProcess(GWS2DBS_CREATE_MAIL , _HandleCmd_CreateMail
37、 , "CREATE_MAIL"); _AddProcess(GWS2DBS_DELETE_MAIL , _HandleCmd_DeleteMail , "DELETE_MAIL"); _AddProcess(GWS2DBS_QUERY_MAIL , _HandleCmd_QueryMail , "QUERY_MAIL"); _AddProcess(GWS2DBS_REFRESH_FRIEND_LIST , _HandleCmd_RefreshFriendList, "REFRESH_FRIEND"); _AddP
38、rocess(DBS_FACTION_CAL_EXP , _HandleCmd_FactionCalExp, "CAL_FACTION_EXP"); / 删除所有主人为空的道具, 这是一项对玩家的道具表的维护操作, 因为对于删除玩家道具在本实/ 例中是处理成把它的拥有者ID置0的做法 sprintf(_szSQL, "DELETE FROM %s WHERE cha_id=0", ITEM_TB); _RunSQL(_szSQL); _pL->L("remove pets and items from db where cha_id =
39、0n"); DebugOut(0); #ifdef LINUX / 这里启动定时器,就好比闹钟一样,到时它会通知DBS执行备份操作 struct itimerval value, ovalue; signal(SIGALRM, pfnBackupRoutine); value.it_value.tv_sec = 5; value.it_value.tv_usec = 0; value.it_interval.tv_sec = 5; value.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &value, &ovalue
40、);#endif return true;/ 添加新的GWSbool CGWSMgr:AddGWS(CGWS *pGWS) _GWSList.push_back(pGWS); char szGWSIP255; pGWS->m_Link->GetRemoteAddr(szGWSIP , NULL , NULL); LOG(LV_M , "Add New GWS %s" , szGWSIP); return true;/ 整个DBS程序运行的主体循环int CGWSMgr:Run() while(m_ListenSock.CanAccept() / 监听socket
41、检查是否有新的gws连接上来CGWS *pNewGWS = new CGWS; pNewGWS->m_Link = new CMFTCPSession; if(pNewGWS->m_Link->Accept(&m_ListenSock) AddGWS(pNewGWS); else delete pNewGWS; LOG(LV_H , "Accept GWS Failed"); break; _DeadList.clear(); / 下面这个循环依次检查当前所有的gws连接,把已经断开或没有响应的连接放到另外一个链表里,随后会清/ 空它, 对于保持着
42、连接的gws,检查是否收到新的消息list<CGWS*>:iterator it; for(it = _GWSList.begin(); it!=_GWSList.end(); it+) CGWS *pGWS = (*it); pGWS->m_Link->DefProc(); / Check Connection Deadif(pGWS->m_Link->IsDead() _DeadList.push_back(pGWS); continue;/ 检查是否收到消息while(pGWS->m_Link->GetPacket(&_RecvCm
43、d) _HandleCmd(pGWS, &_RecvCmd);/进入消息处理 / 清除已经断开的连接 for(it = _DeadList.begin(); it!=_DeadList.end(); it+)CGWS *pDeadGWS = (*it); _GWSList.remove(pDeadGWS); _pL->L("clear dead GWS %d : %s", pDeadGWS->GetID(), Util_CurTime2String(0); delete pDeadGWS; SysSleep(20); / 还给cpu一定的时间 _nRun
44、Cnt+; / 定时检查帮派的数据表,检查帮派的有效性 int nNow = Util_GetSysTick(); if(_nFactionCheckTick=0) _nFactionCheckTick = nNow; if(_nCalFactionExpTick=0) _nCalFactionExpTick = nNow; if(nNow - _nFactionCheckTick) >= (int)(60.0f * 1000.0f * _fFactionCheckInterval) _CheckFactionValid(); _nFactionCheckTick = nNow; / 定
45、时计算帮派的总经验值, 并更新数据表的相应数据 if(nNow - _nCalFactionExpTick) >= (int)(60.0f * 1000.0f * _fCalFactionExpInterval) RunCmd(DBS_FACTION_CAL_EXP); _nCalFactionExpTick = nNow; return 1;/ 下面是一个调试输出的函数, 可以察看到所有的消息对应的处理函数累计所花费的时间,从而可以了解那些操/ 最影响程序的性能void CGWSMgr:DebugOut(int nFlag) _pL->L("nProcess Perfo
46、rmance : %s", Util_CurTime2String(0); for(map<short , SProcess>:iterator it = _ProcessList.begin(); it!=_ProcessList.end(); it+) short sMsgType = (*it).first; SProcess *pProcess = &(*it).second); _pL->L("Process %d %-18s Time = %d msn", sMsgType , pProcess->szName, pPr
47、ocess->nTimeCnt); / 封装和简化对GWS消息的发送, / pCmd参数包含了要发送的数据包的指针/ pGWS表示发送给哪一个GWS,此参数如果为空,则会发送给所有的GWS/ 参数bExcept表明是否除了指定的gws以外,向其它的所有gws发送消息/ 参数bEndWrite是packet读写类所需要的一项特定操作,无需在意void CGWSMgr:_SendMsg(CMFCmdPacket *pCmd, CGWS *pGWS, bool bExcept, bool bEndWrite) if(bEndWrite) pCmd->EndWrite(); if(pGWS
48、=NULL) / All the GWS Will Receive Msg for(list<CGWS*>:iterator it = _GWSList.begin(); it!=_GWSList.end(); it+) (*it)->m_Link->SendPacket(pCmd); else if(bExcept) / Except pGWS , Other GWS will Recive Msg for(list<CGWS*>:iterator it = _GWSList.begin(); it!=_GWSList.end(); it+) if(*it
49、)!=pGWS) (*it)->m_Link->SendPacket(pCmd); else / Only pGWS will Receive Msg pGWS->m_Link->SendPacket(pCmd); / 针对所有的GWS,检查某个玩家是否在线CGamePlayer* CGWSMgr:CheckPlayerOnline(long lID, CGWS *ppGWS) for(list<CGWS*>:iterator it = _GWSList.begin(); it!=_GWSList.end(); it+) CGWS *pGWS = (*it)
50、; CGamePlayer *pPlayer = pGWS->CheckPlayerOnline(lID); if(pPlayer) *ppGWS = pGWS; return pPlayer; return NULL;/ 消息处理函数, 所有的消息处理都通过此函数进入void CGWSMgr:_HandleCmd(CGWS *pGWS , CMFCmdPacket *pCmd) short sMsgType; pCmd->ReadShort(&sMsgType);/解析出消息类型 LOG(LV_M , "Receive Message - %d : " , sMsgType); map<short, SProcess>:iterator it = _ProcessList.find(sMsgTyp
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年口腔医疗管理公司院感防控培训管理制度
- 广西河池市宜州区2024-2025学年八年级上学期期末生物试题(含答案)
- 护理部护理服务特色汇报
- 紧急护理人力资源应急响应机制
- 债权人公告制度
- 信贷员尽职免责制度
- 住院总医师岗位制度
- 企业询价制度
- 成功案例|如何进行工时制度改革与定岗定编?-华恒智信车辆检测维修企业降本增效实践案例解析
- 产品开发委托制度
- 2025年汽车零部件行业市场调研:细分品类、技术创新及配套需求报告
- 用pdca降低会阴切开率课件
- 催收高手实战话术
- 2026年化妆培训服务合同
- 人教版小学五年级上册科学期末试卷后附答案
- 2025年七年级上册历史知识点梳理(背诵版)
- 雨课堂学堂云在线《人工智能原理》单元测试考核答案
- 航空航天配套产业招商创新创业项目商业计划书
- 稻草人艺术活动方案
- 线性代数课件 第6章 二次型 第3节
- 2025年国家开放大学(电大)《市场营销原理与实践》期末考试备考题库及答案解析
评论
0/150
提交评论