插件开发技术说明(17)---应用CRUD开发模式范例

对一个数据库表进行增,删,修改,查询操作是常见的编程任务.
统一和简化此类开发任务,可以提高开发效率,包括实现,调试和维护.

本文以常购商品的功能为例,说明基于需求完整实现的过程.

1.功能与协议

提供门店经常采购商品信息的管理,提供对快捷订单的支持.

一个协议的功能单一化,避免让一个协议承载太多功能,协议与用例一一对应作为设计原则。
常购商品的4协议如下:
.新增常购商品(7293)
支持批量新增
.修改常购商品(7294)
支持批量修改
.删除常购场频(7295)
支持批量删除
.查询常购商品(7296)

常购商品数据维护的协议都设计为支持批量操作.
并非所有操作都要支持批量,取决于交互操作界面规定的方式,批量处理一般对应一个列表界面,记录行有一个勾选框。
常见的批量操作有删除,状态改变。
新增和修改并不总是要求批量操作。

2.实现

以下内容在设计阶段规定:

.相关表结构:本例有t_Ven_PreferGoods
.协议在哪个插件中实现:本例为wiser插件
.新增哪些类,类的头文件,实现文件(cpp):如果可被其它插件访问则把文件放到common目录下.

*协议设计时要求协议的参数和行集列名称与数据库表一致.

2.1根据表结构定义类.

常购商品表(t_Ven_PreferGoods)结构:
EId int not null comment '购买者企业id',
GoodsID int not null comment '商品id',
CoEId int not null comment '供货商企业id',
PackQty int not null comment '常购件数'

CPreferGoods类定义:在wiser_data.h

#include "slic_data_env.h"
#include "orm_base.h"
//////////////////////////////////////////////////////////////////////////////
///< 常购商品
class CPreferGoods : public CORMBase {
public:
	class CQueryCond { ///< 常购商品查询条件
	public:
		string key_value_;
	};
public:
	SLIC_EID eid_; ///< 购买者企业ID
	SLIC_GOODS_ID goods_id_; ///< 商品ID
	SLIC_EID co_eid_; ///< 供货者企业ID
	int pack_qty_; ///< 常购数量(件数)
public:
	CPreferGoods():eid_(0),goods_id_(0),co_eid_(0),pack_qty_(0) {
	}


	BIND_DEFINE(CPreferGoods);


	static int Query(CQueryCond &cond,vector<CPreferGoods*> &data); ///< 查询常购商品
};
CPreferGoods从CORMBase派生.
插件工程中加入common\orm_base.cpp.

增加CPreferGoods绑定(wiser_data.cpp)
/////////////////////////////////////////////////////////////////////////////
BIND_DECLARE_BEGIN(CPreferGoods,"t_Ven_PreferGoods")
	FIELD_BIND3(CPreferGoods,eid_,"EId",true),
	FIELD_BIND3(CPreferGoods,co_eid_,"CoEId",true),
	FIELD_BIND3(CPreferGoods,goods_id_,"GoodsID",true),
	FIELD_BIND1(CPreferGoods,pack_qty_,"PackQty"),
BIND_DECLARE_END(CPreferGoods)


/////////////////////////////////////////////////////////////////////////////
int CPreferGoods::Query(CQueryCond &cond,vector<CPreferGoods*> &data) {
	return CE_UNIMPLEMENT; ///< 未实现
}
未实现CPreferGoods::Query的原因是,协议7296处理函数利用了SQLBasedMsgProc直接从SQL查询返回消息包,不需要生成对象的过程,更有效.

如果查询后还需要后续处理,则采用对象化方式更合适.

在插件Initialize中增加初始化绑定(CPreferGoods::InitBinder)

int CWiser::Initialize() {
    parent::Initialize();
    CDataEnv::Init(); 
    INIT_DATAENV_OBJECT(CDataEnv::env_,this->local_dbc_);

    CPreferGoods::InitBinder();
}

2.2协议实现

在插件类中(wiser.h文件中)增加协议处理函数定义.
class CWiser
: public CQQBasePlugin {
	///< 查询常购商品(7293)
    int OnQueryPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or);
	///< 新增操作常购商品(7294)
    int OnAddPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or);
	///< 修改常购商品信息(7295)
    int OnChangePreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or);
	///< 删除常购商品(7296)
    int OnDeletePreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or);
};
 在插件类实现文件(wiser.cpp)中增加协议处理函数映射,实现各个协议处理函数.
协议处理函数只列举了7294和7293的对应函数.7295,7296的处理与7294类似,差别是调用CPreferGoods的Update,Delete方法。
MSG_FUNC_MAP CWiser::func_[] = {
    {7293,MT_REQUEST,(MSGFUNC)&OnQueryPreferGoods,true,"查询常购商品",""},
    {7294,MT_REQUEST,(MSGFUNC)&OnAddPreferGoods,true,"新增常购商品",""},
    {7295,MT_REQUEST,(MSGFUNC)&OnChangePreferGoods,true,"修改常购商品信息",""},
    {7296,MT_REQUEST,(MSGFUNC)&OnDeletePreferGoods,true,"删除常购商品",""},
};
////////////////////////////////////////////////////////////////////////////////
///< 新增操作常购商品(7294)
int CWiser::OnAddPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or) {
    CMsg *msg = in->msg;
    long lEId = 0, lUserID = 0;
    CHECK_MSG_PARAMETER2(msg,"EId",&lEId,-1); ///< 检查并读取消息参数,其它类似宏见BasePlugin.h中的定义
    CHECK_MSG_PARAMETER2(msg,"UserID",&lUserID,-1);


    CRowset *rs = 0;
    CHECK_MSG_ROWSET(msg,0,rs,-1); ///< 检查消息中的行集
    int rec_num = rs->GetRSMeta(RST_ROW_CNT);
    GETDBC(pdbor,this->local_dbc_.c_str());
    FAIL_RETURN(pdbor->BeginTrans(),,-1);
    for (int i=0;i<rec_num;i++) {
        CPreferGoods pg;
        pg.eid_ = lEId;
        if (pg.LoadFrom(rs,i)) ///< 从消息行集记录加载对象成员
            return -1;
        if (-1==pg.Insert(1))
            return -1;
    }
    FAIL_RETURN(pdbor->CommitTrans(),,-1);


    return 0;
}

////////////////////////////////////////////////////////////////////////////////
///< 查询常购商品(7293)
int CWiser::OnQueryPreferGoods(CWrappedMsg<> *in,vector<CWrappedMsg<>*> &out,DISPATCH_RESULT &or) {
    CMsg *msg = in->msg;
    long lEId = 0, lUserID = 0;
    CHECK_MSG_PARAMETER2(msg,"EId",&lEId,-1);
    CHECK_MSG_PARAMETER2(msg,"UserID",&lUserID,-1);
    long co_eid=0;
    msg->GetParam("CoEId",co_eid);
    string key_value;
    msg->GetParam("KeyValue",key_value);
    bool has_key_value = !key_value.empty();
    string cond;
    ///< 处理查询条件
    if (has_key_value) {
        int key_value_prop = CheckStringProp(key_value); ///< 检查字符串特性:全数字,含汉字
        if (key_value_prop&0x01) { ///< 全数字
            cond = LogMsg(" (b.barcode like '%%%s%%' or b.mygoodsid like '%%%s%%') ",key_value.c_str(),key_value.c_str());
        }
        else if (key_value_prop&0x02) { ///< 含汉字
            cond = LogMsg("b.goodsname like '%%%s%%'",key_value.c_str());
        }
        else {
            cond = LogMsg(" (b.helpcode like '%%%s%%' or b.mygoodsid like '%%%s%%')",key_value.c_str(),key_value.c_str());
        }
        cond += " and ";
    }
    if (co_eid) {
        cond += LogMsg("a.coeid=%lu",co_eid);
        cond += " and ";
    }
    if (!cond.empty()) 
        cond.erase(cond.length()-5,5);

    ///< 生成查询SQL
    string sql = LogMsg("select a.CoEid as EId, a.GoodsID,DataVersion,PicVersion from t_ven_PreferGoods a,t_bas_mygoods b"
        " where a.eid=%lu and a.coeid=b.eid and a.goodsid=b.goodsid %s %s order by EId,GoodsID",lEId,!cond.empty() ? "and":"",cond.c_str());


    return SQLBasedMsgProc(sql,in,out,or); ///执行SQL把结果返回给客户端
}

3.特殊处理


(1)从消息参数或行集获取对象属性后,需要额外设置其它未包含在消息包中的属性时:
使用:int AddFields(unsigned short offset,...) 
示例:
	CPendingOrg po;
	CMsg *req = new CMsg;
	req->SetMsgType(MT_REQUEST);
	req->SetMsgID(1);
	req->AddParam("MyOrgID","1");
	req->AddParam("OrgType",2L);
	po.LoadFrom(req);
	
	///< CreateTime,Reason字段对应的属性未包含在req消息参数中
	///< 仅设置成员并不能让底层知晓需要修改对应字段,需要显式设置需要修改的字段.
	CDateTime::GetDateTime(po.create_time_);
	po.reason_ = 11;
	po.AddFields(OFFSETOF(CPendingOrg,reason_),OFFSETOF(CPendingOrg,create_time_),-1); ///< 指定需要更新的成员变量,支持一次指定多个成员,以-1作为结束标志
	if (po.Update())
		return -1;

4.相关代码说明

CORMBase类:

头文件:orm_base.h
实现文件:orm_base.cpp
//////////////////////////////////////////////////////////////////////////////
class CORMBase {
protected:
	int GenKeyCond(string &cond); ///< 生成条件表达式串
public:
	virtual string& GetTableName() = 0;
	virtual vector<CFieldBind*>& GetKeyFields() = 0;
public:
	virtual CRecordsetBindObjectBase* GetBinder() { return 0; }
	virtual CRecordsetBindObjectBase* NewBinder(const char *fld,...) { return 0; }
	virtual CRecordsetBindObjectBase* NewBinder(const char *fld,va_list arg) { return 0; }
	///< 检查对象数据是否有效
	///< @return -1:错误 0:无效 1:有效
	virtual int Check() { return 1; }


	/// @return 0-成功 -1-失败 1-存在主键冲突
	virtual int Insert(short on_dup_error=2); ///< on_dup_error 忽略-1,按错误处理-2,更新-3 
	virtual int Update(); ///< 修改对象
	virtual int Update(const char *fld,...); ///< 修改指定的字段
	virtual int Select(const char *fld,...); ///< 查询指定的字段
	virtual int Delete(); 


	virtual int Load(); ///< 从数据库加载对象


	virtual int LoadFrom(CMsg *msg); ///< 从消息参数加载对象成员
	virtual int LoadFrom(CRowset *rs,unsigned long recno); ///< 从消息行集记录加载对象


	virtual int SaveTo(CMsg *msg); ///< 把对象成员输出到消息参数
	virtual int ToField(CRowset *rs); ///< 把对象成员输出到行集列信息
	virtual int SaveTo(CRowset *rs); ///< 把对象成员输出到行集记录(新增记录)


	virtual int LoadFrom(CRecordset *rs); ///< 从查询记录集加载对象成员
};


猜你喜欢

转载自blog.csdn.net/wherwh/article/details/49029599
今日推荐