本文描述以下内容
.如何在插件中实现一个协议
.如何设置协议不需要安全验证:即在用户登录前可以访问
.如何预加载数据缓存
.面向对象的业务逻辑处理:业务对象封装属性和逻辑,协议处理函数负责调用,而不直接涉及业务逻辑
.如何查询数据库
.如何实现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;
}