插件开发技术说明(2)---rc_finder插件

本文描述以下内容
.如何在插件中实现一个协议
.如何设置协议不需要安全验证:即在用户登录前可以访问
.如何预加载数据缓存
.面向对象的业务逻辑处理:业务对象封装属性和逻辑,协议处理函数负责调用,而不直接涉及业务逻辑
.如何查询数据库
.如何实现ORM操作数据库
.如何编写跨数据库程序
.协议处理函数的一种常见形式


1.增加一个新协议

在rc_finder.cpp的消息函数映射中增加新协议的映射项:

MSG_FUNC_MAP CRCFinder::func_[] = {
 {214,MT_REQUEST,(MSGFUNC)&CRCFinder::OnQuerySiteList,true,"查询主站列表",""}, 
};

含义是:214-Request消息由插件CRCFinder::OnQuerySiteList函数处理,该协议的名称是"查询主站列表"。

在rc_finder.h的插件类CRCFinder中增加OnQuerySiteList的定义.

class CRCFinder
: public CQQBasePlugin
{
 /// 214-Request 查询主站列表
 int OnQuerySiteList(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or);
};

在rc_finder.cpp中实现CRCFinder::OnQuerySiteList函数.

2.设置协议不需要安全验证

int CRCFinder::Initialize() {
 parent::Initialize();

 ///< 设置214协议不需要用户验证
 sec_->SetProtAuthProp(0,214,false);
 return 0;
}

3.预加载数据缓存

插件负责缓存的数据在Activate函数中加载.如果需要提供其它插件访问,则定义接口并通过框架容器接口登记.(后续提供示例)
rc_finder插件在启动时把所有主站信息加载到内存. (这里没有考虑缓存更新问题)

int CRCFinder::Activate() {
 parent::Activate();

 ///< 初始化数据环境
 CDataEnv::Init();
 CDataEnv::env_->dbc_name_ = local_dbc_;
 ///< 加载主站信息
 if (LoadSite()) {
  return -1;
 }
 return 0;
}

4.协议处理函数示例

int CRCFinder::OnQuerySiteList(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {
 CMsg *msg = in->msg; ///< in->msg为接收到的消息对象
 int last_data_version = GetDataLastVersion(31); ///< 取主站信息的最新数据版本. 31为主站的主数据类型编码,见Yoop概要设计文档.
 if (last_data_version==-1) 
  return -1;

  ///< 创建返回消息,消息类型为MT_CONFIRMATION.协议通信模式:请求-确认
  ///< 消息格式见应用协议定义文档
 CMsg *ans = new CMsg;
 ans->SetMsgType(MT_CONFIRMATION);
 ///< 返回主站信息的最新数据版本,客户端可以比较本地版本后决定是否更新
 ans->AddParam("DataVersion",(long)last_data_version);
 ///< 创建行集对象并加入到消息中
 CRowset *rs = new CRowset;
 ans->AddRowset(rs);
 ///< 设置行集列,SetFields最后以参数0结束.
 rs->SetFields("SiteId","SiteName","ServerIP","ServerPort","ServerIP2","ServerPort2",0);
 ///< 遍历预加载的主站信息,加入行集
 CAutoVector<CSite*>::iterator iter = sites_.begin();
 while(iter!=sites_.end()) {
  CSite *site = *iter;
  if (site->is_default_) { ///< 把默认主站以参数加入消息
   ans->AddParam("PrimarySiteID",(long)site->id_);
  }
  char szSiteId[12],szPort[12],szPort2[12];
  CSiteRC *rc1 = site->GetRC(SITE_SERVER);
  CSiteRC *rc2 = site->GetRC(SITE_SERVER2);
  sprintf(szSiteId,"%d",site->id_);
  sprintf(szPort,"%d",rc1->svc_addr_.port_);
  sprintf(szPort2,"%d",rc2->svc_addr_.port_);
  ///< 加入一个主站记录,参数个数与SetFields的列数相同.AddRecord所有参数都是char*,以参数0结束.
  ///< 加入记录的另一种形式:调用无参数的AddRecord加入一条空记录,然后用SetFieldValueByName逐列设置值.
  rs->AddRecord(szSiteId,site->name_.c_str(),rc1->svc_addr_.ip_.c_str(),szPort,rc2->svc_addr_.ip_.c_str(),szPort2,0);
  iter++;
 }

  ///< 在源通道上返回消息
 DO_RESP(ans,out);

  ///< 返回0表示成功,其他值表示失败
 return 0;
}

5.查询数据库

以下是插件启动时加载主站信息缓存的函数.

int CRCFinder::LoadSite() {
  ///< 查询SQL语句
 string sql = "select sid,sname,isdefault from t_sys_site where status=1 order by sid";
 ///< 获取插件本地数据库的连接对象
 GETDBC(pdbor,local_dbc_.c_str());
 ///< 声明自动管理生命期的查询记录集(CRecordset)对象.
 AUTO_QUERY_RECORDSET(CRecordset,prs,pdbor);
 ///< 执行查询SQL,返回记录集对象
 prs = pdbor->Query(adCmdText,sql.c_str());
 if (prs==0) {
  return -1;
 }
 _variant_t vtSiteId,vtSiteName,vtIsDefault;
 string strSiteId,strSiteName,strIsDefault;
 ///< 遍历记录集
 while(!prs->IsEof()) {
   ///< 读记录集字段
  if (!prs->GetFieldValue("sid",vtSiteId)||
   !prs->GetFieldValue("sname",vtSiteName)||
   !prs->GetFieldValue("isdefault",vtIsDefault))
   return -1;
  ///< 把_variant_t转换为字符串
  strSiteId = ExVariantToString(vtSiteId);
  strSiteName = ExVariantToString(vtSiteName);
  strIsDefault = ExVariantToString(vtIsDefault);

  ///< 创建主站对象,设置对象成员
  CSite *site = new CSite;
  site->id_ = atoi(strSiteId.c_str());
  site->name_ = strSiteName;
  site->is_default_ = atoi(strIsDefault.c_str())==1;
  if (site->is_default_)
   this->default_site_ = site;

  ///< 加载主站资源信息
  if (site->LoadRC())
   return -1;

  ///< 把主站对象加入容器中
  sites_.push_back(site);
  if (!prs->Move())
   return -1;
 }
 return 0;
}

6.面向业务对象

一切皆对象.
slic应用系统中的对象包括:
CSite:主站对象
CSiteRC:主站资源对象
CEnterprise:企业对象
CUser:用户对象
CCategory:商品类别对象
CGoods:商品对象
CPicture:图片对象
CBrand:品牌对象
CArea:区域对象

7.附:slic_data.h

/// slic对象定义
#ifndef H_SLIC_DATA
#define H_SLIC_DATA

#include "auto_pointer.h"
#include "i_log.h"
#include "i_db_oper.h"

//
namespace slic_ns {
typedef unsigned int SLIC_SITE_ID; ///< 主站类型
typedef unsigned int SLIC_EID; ///< 企业ID

///< 资源类型
enum SLIC_RCTYPE {
	SITE_SERVER = 5, ///< 主站服务器
	SITE_SERVER2 = 6, ///< 主站匿名服务器
};

enum SLIC_ORG_TYPE {
	ORG_TYPE_UNKNOWN = 0, ///< 未知
	ORG_TYPE_SUPPLIER = 20,///	供应商
	ORG_TYPE_PRODUCER = 21,///	生产商
	ORG_TYPE_STORE = 31,///	门店
};

//
///< 数据环境
struct CDataEnv {
	string dbc_name_; ///< 数据库连接名称
	INative_Logger_Base *logger_; ///< 日志对象
	IDbHelper*	db_helper_; ///< 数据库操作助手
	static int Init();
	static void Cleanup();
	static CDataEnv *env_;

};
//
	///< 资源信息
struct CRC {
	SLIC_RCTYPE type_;
	string value_;
};

//
struct HostAddress {
	string ip_;
	unsigned short port_;
};
//
/// 主站资源
struct CSiteRC : public CRC {
	HostAddress svc_addr_;  ///< 服务器地址

	static CSiteRC* Get(SLIC_SITE_ID site_id); ///< 获取site_id主站的对象
};
//
///< 主站信息
class CSite {
	CAutoVector<CSiteRC*> rcs_; ///< 主站资源
public:
	SLIC_SITE_ID id_;
	string name_;
	bool is_default_;
public:
	///< 加载主站资源信息
	int LoadRC();
	///< 查找指定类型的资源
	CSiteRC* GetRC(SLIC_RCTYPE type); 

	static SLIC_SITE_ID GetDefaultSiteID();
};

//
///< 企业信息
struct CEnterprise {
	SLIC_EID id_; ///< 企业ID
	string name_; ///< 企业名称	
	SLIC_ORG_TYPE type_; ///< 企业类型	
	unsigned int area_id_; ///< 区域编码	
	string addr_; ///< 地址	
	short postcode_; ///< 邮编	
	string tele_; ///< 电话	
	string fax_;  ///< 传真
	string email_; ///< 邮箱
	string mgr_; ///< 负责人	
	string mgr_tele_; ///< 负责人电话	
	string mgr_mobile_; ///< 负责人手机	
	string contract_; ///< 联系人
	string contract_mobile_; ///< 联系人手机

	unsigned int site_id_; ///< 分配的主站ID
public:
	CEnterprise():id_(0),type_(ORG_TYPE_UNKNOWN),area_id_(0),postcode_(0),site_id_(0) {
	}
	int AssignSite(); ///< 分配主站
	int Save(); ///< 保存注册信息

	static SLIC_EID ApplyID(SLIC_ORG_TYPE org_type); ///< 获取一个可用的企业ID
};

//
///< 用户
class CUser {
public:
};

//
///< 商品类别
class CCategory {
public:
};

//
///< 商品
class CGoods {
public:
};

//
///< 图片
///< 商品,类别,广告图片的基类
class CPicture {
public:
};

//
///< 品牌
class CBrand {
public:
};

//
///< 区域对象
class CArea {
public:
}

};

#endif


 

8.附:slic_data.cpp

CEnterprise::Save演示了如何在对象和数据库之间绑定,Insert记录的方法。

CEnterprise::ApplyID演示了如何编写跨数据库的程序.

#include "slic_data.h"
#include "baseplugin.h"
#include "stdstring.h"
#include "stdfunc.h"
#include "query_func.h"

using namespace slic_ns;
CDataEnv*  CDataEnv::env_ = 0;

int CDataEnv::Init() {
	env_ = new CDataEnv;
	return 0;
}

void CDataEnv::Cleanup() {
	if (env_) delete env_;
}


int CSite::LoadRC() {
	CAutoVector<CSiteRC*>::iterator iter = rcs_.begin();
	string sql = LogMsg("select rctype,rcvalue from t_Sys_SiteRC where sid=%d and status=1",this->id_);
	USEDBC(pdbor,CDataEnv::env_->dbc_name_.c_str());
	AUTO_QUERY_RECORDSET(CRecordset,prs,pdbor);
	prs = pdbor->Query(adCmdText,sql.c_str());
	if (prs==0) {
		return -1;
	}
	_variant_t vtRcType,vtRcValue;
	string strRcType,strRcValue;
	while(!prs->IsEof()) {
		if (!prs->GetFieldValue("rctype",vtRcType)||
			!prs->GetFieldValue("rcvalue",vtRcValue)) {
				return -1;
		}
		strRcType = ExVariantToString(vtRcType);
		strRcValue = ExVariantToString(vtRcValue);
		SLIC_RCTYPE rc_type = (SLIC_RCTYPE)atoi(strRcType.c_str()); 
		CSiteRC *rc = new CSiteRC;
		rc->type_ = rc_type;
		rc->value_ = strRcValue;
		if (rc_type==SITE_SERVER||rc_type==SITE_SERVER2) { 
			vector<string> vs;
			int num = SplitString(strRcValue.c_str(),vs,':');
			if (num!=2) { ///< 主机服务地址格式错误
				if (!prs->Move()) 
					return -1;
				delete rc;
				continue; 
			}
			rc->svc_addr_.ip_ = vs[0];
			rc->svc_addr_.port_ = atoi(vs[1].c_str());
		}
		rcs_.push_back(rc);
	
		if (!prs->Move()) 
			return -1;
	}
	return 0;
}


CSiteRC* CSite::GetRC(SLIC_RCTYPE type) {
	CAutoVector<CSiteRC*>::iterator iter = rcs_.begin();
	while(iter!=rcs_.end()) {
		CSiteRC *rc = *iter;
		if (rc->type_==type) 
			return rc;
		iter++;
	}

	return 0;
}


SLIC_SITE_ID CSite::GetDefaultSiteID() {
	string sql = "select SId from t_Sys_Site where Status=1 and IsDefault=1";
	USEDBC(pdbor,CDataEnv::env_->dbc_name_.c_str());
	unsigned long id = 0;
	int ret = CQueryFunc::Query(pdbor,sql,id);
	if (ret!=1) {
		return -1;
	}
	return (SLIC_EID)id;
}


SLIC_EID CEnterprise::ApplyID(SLIC_ORG_TYPE org_type) {
  ///< 插件函数中使用GETDBC获取连接,插件外使用USEDBC
	USEDBC(pdbor,CDataEnv::env_->dbc_name_.c_str());
	///< 利用数据库扩展支持不同的数据库,GetDBExt返回IDbExt接口.
	string sql = LogMsg("select %s FreeId from t_Sys_EIdRes %s where Flag=1 and Status=0 and (OrgType=0 or OrgType=%d) %s %s",
			pdbor->GetDBExt()->Top(1),
			pdbor->GetDBExt()->WithLock().c_str(),
			org_type,
			pdbor->GetDBExt()->Limit(1).c_str(),
			pdbor->GetDBExt()->ForUpdate().c_str());
	unsigned long id = 0;
	int ret = CQueryFunc::Query(pdbor,sql,id);
	if (ret!=1) {
		return -1;
	}
	return (SLIC_EID)id;
}


int CEnterprise::AssignSite() {
	string sql = LogMsg("select SId from t_Sys_SiteArea where AreaId=%d",this->area_id_);
	USEDBC(pdbor,CDataEnv::env_->dbc_name_.c_str());
	unsigned long site_id = 0;
	int ret = CQueryFunc::Query(pdbor,sql,site_id);
	if (ret!=-1)
		return -1;
	if (ret==0) {
		return CSite::GetDefaultSiteID();
	}
	return (int)site_id;
}

int CEnterprise::Save() {
	///< 获取数据库连接
	USEDBC(pdbor,CDataEnv::env_->dbc_name_.c_str());
	///< 获取t_Sys_Org表的处理器对象
	ITableHandler *th = CDataEnv::env_->db_helper_->NewTableHandler(pdbor,"t_Sys_Org");
	///< 自动管理th生命期
	AUTO_POINTER_NODECLARE(ITableHandler,th);
	///< 把企业对象成员变量与数据库表字段绑定
	///< 对象成员类型不同,调用对应的BindField方法.
	th->BindField("EId",(char**)&this->id_,sizeof(this->id_)); 
	th->BindField("EName",this->name_);
	th->BindField("OrgType",(char**)&this->type_,sizeof(this->type_));
	th->BindField("Address",this->addr_);
	th->BindField("Postcode",(char**)&this->postcode_,sizeof(this->postcode_));
	th->BindField("Tele",this->tele_);
	th->BindField("Email",this->email_);
	th->BindField("FaxNo",this->fax_);
	th->BindField("Manager",this->mgr_);
	th->BindField("ManagerTel",this->mgr_tele_);
	th->BindField("ManagerMobile",this->mgr_mobile_);
	th->BindField("Contract",this->contract_);
	th->BindField("Contract",this->contract_mobile_);
	th->BindField("AreaId",(char**)&this->area_id_,sizeof(this->area_id_));
	th->BindField("SiteId",(char**)&this->site_id_,sizeof(this->site_id_));
	
	///< 执行Insert
	///< 如果有chunk字段,则调用Insert2方法.
	///< 对应的Update操作的方法分别是Update,Update2.
	if (th->Insert(false)) {
		return -1;
	}

	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值