控件数据库数据库初始化创建和组织数据库保存数据库插入一个数据库设置当前数据库值打开和关闭数据库对象删除对象删除数据库对象添加对象指定数据对象的关闭实体对象容器对象扩展记录
控件数据库

数据库是控件最重要的部分,所有图形信息都储存在数据库中,因此掌握数据库是控件开发的基础。数据库对象包括实体、块表记、符号表记录和字典,数据库对象组成了数据库。数据库的结构如下图:


db.png


数据库概述


控件数据库中存储了构成图形的对象和实体。图形是存储在数据库中对象的集合,基本的数据库对象包括实体、符号表和字典。


实体是一种指定类型的数据库对象,它在控件屏幕上有图形表示,实体的例子有直线、圆、圆弧、文字、曲线和椭圆等。用户在屏幕上可以看见实体并且可以对它进行操作。


符号表和字典是用来存储数据库对象的容器对象,这两种对象都可以将一个符号名映射到一个数据库对象。控件数据库包括固定数目的符号表,每一个符号表包含了一种特定类的实例作为符号表记录。符号表的例子如层表(McDbLayerTable,包含层表记录)和块表(McDbBlockTable,包含块表记录),所有实体都属于块表记录。


字典提供了一个比符号表更加通用的容器对象来存储对象,一个字典可以包含任何McDbObject类或其他子类的对象


在一个使用控件过程中,可以装入多个数据库,其中每一个对象都有自己的句柄和对象ID。这个唯一的句柄用来在一个特定的数据库中识别对象,而唯一的对象ID用来在同一时间装入的数据库中识别对象。


    对象ID只存在于编辑过程中,而句柄可以保存在图形中。与对象ID相比,当在一个编辑过程中装入多个数据库时,句柄不一定是唯一的。


通过对象ID,用户可以获得一个实际数据库对象的指针,这样用户就可以对对象执行操作。用户可以通过下面的方法获得对象ID:


    1.   建一个对象并将它添加到数据库中。数据库会赋予对象一个对象ID并且返回给用户。

    2.  使用一个遍历器来遍历一组对象。McDb库提供了一些遍历器来浏览不同类型的容器对象(McDbDictionaryIterator和McDbObjectIterator)。

    3.  查询一个选择集。当选择了一个对象后,用户可以询问选择集来获得所选择对象的实体名列表,然后将这些实体名转换成对象ID。

    4.  由实体名柄得到实体ID

    5.  由实体名得到实体ID

数据库初始化

控件数据库中包含了以下元素:


符号表和对应类如下:


名称 类型
块表 McDbBlockTable
层表 McDbLayerTable
线型表 McDbLinetypeTable
文本样式表 McDbTextStyleTable


    一些符号表中已经包含了一个或多个记录。如层表中有一个记录0层,块表中有个*MODEL_SPACE记录,而线型表中包含一个记录:CONTINUOUS。


命名对象字典:

创建和组织数据库

用户可以使用new命令创建一个数据库,使用delete命令删除一个数据库。 可以使用下面的函数读取一个图形文件:


Mcad::ErrorStatus McDbDatabase::readDwgFile(LPCTSTR filename);


    注意:一定不要删除由Mx::mcdbHostApplicationServices()->workingDatabase()函数返回的数据库,因为它返回的是当前工作的数据库。

保存数据库

要保存一个数据库,可以使用McDbDatabase::saveAs()函数。


saveAs function


作用:保存一个数据库。


接口Mcad::ErrorStatus saveAs(

LPCTSTR pszFileName,

           const voidpSecParams = 0,

           LPCTSTR wszPassword = NULL,

           McDb::McDbDwgVersion version = McDb::kDHL_1015,

           McDb::SaveType type = McDb::kDwg,

           byte** ppRetData =NULL,

           longpRetDataLength = NULL


参数


名称 说明
pszFileName

DWG文件路径

pSecParams

暂没使用

wszPassword

暂没使用

version

DWG文件版本

type

文件类型,可以是DWG或DXF

ppRetData

把数据保存到内存中,返回的数据需要free函数释放

pRetDataLength

返回数据的长度


返回值:如果成功返回Mcad::eOk,如果传递的数据非法则返回Mcad::eInvalidInput。


参考例程


McDbDatabase * pDatabase1 = new McDbDatabase;
auto dsa = pDatabase1->readDwgFile(L"D:\\MxDraw\\Tech\\database\\测试.mxg");
pDatabase1->saveAs(
L"D:\\MxDraw\\Tech\\database",
nullptr, nullptr, McDb::kDHL_1015, McDb::kDxf);
插入一个数据库

要保存一个数据库,可以使用McDbDatabase::saveAs()函数。


    如果两个数据库合并时发生冲突(例如两个数据库有相同的线型名),则在控件中使用目标数据库的数据。


下面的函数与标准的图形插入命令等价:


Mcad::ErrorStatus McDbDatabase::insert(McDbObjectId& blockId, LPCTSTR pBlockName, McDbDatabase* pDb);


Insert function


作用:将一个数据库复制到调用这个成员函数的数据库中。


接口Mcad::ErrorStatus insert(

  McDbObjectId & blockId,

           LPCTSTR pszBlockName,

           McDbDatabase* pDb,

           bool preserveSourceDatabase = true,

           bool isUpdataSameNameBlock = true);


参数


名称说明
blockId

返回新插入的图块表记录id

pszBlockName

新插入的图块表记录名,图块为空时,就把文件中所有图块都引进到当前图纸中

pDb

插入的数据库指针

preserveSourceDatabase

暂没使用

isUpdataSameNameBlock

如果插入的数据库的图块名与当前数据库的图块名同名,需要不需要重新更新该图块


返回值:如果成功返回Mcad::eOk,如果传递的数据非法则返回Mcad::eInvalidInput。


参考例程


AcDbDatabase db(Mdesk::kFalse );
     if( db.readDwgFile( sBlkFileName ) != Acad::eOk )
     {
      return FALSE;
     }
     if ( pDatabase->insert( blkRecId, sBlkName, &db ) != Acad::eOk )
     {
      return FALSE;
     }


这个函数将实体从输入数据库的模型空间复制到指定的块表记录中,同时返回新块表记录的ID。

设置当前数据库值

如果用户没有指定一个实体的属性(例如颜色和线型),则实体的属性使用数据库的当前属性。下面我们将对数据库的属性作一个详细介绍。


数据库颜色值


如果实体的颜色没有指定,则实体的颜色将使用存储在CECOLOR系统变量中的数据库当前颜色值。下面的函数可以用来设置和获得数据库当前的颜色值:


Mcad::ErrorStatus McDbDatabase::setCecolor(const McCmColor& color);
McCmColor McDbDatabase::cecolor() const;


数据库线型值


下面的函数可以用来设置和获得数据库当前的线型值:


Mcad::ErrorStatus McDbDatabase::setCeltype(McDbObjectId);
McDbObjectId McDbDatabase::celtype() const;


数据库线型比例值


在数据库中有2个线型比例设置:


    1. 当前实体的线型比例,保存在CELTSCALE系统变量中

    2. 当前图形的线型比例,保存在LTSCALE系统变量中


全局的LTSCALE设置在图形重新生成时使用,可以使用下面的函数来设置或获取这些值:


    设置LTSCALE中的比例 Mcad::ErrorStatus McDbDatabase::setLtscale(double);

    读取LTSCALE中的比例double McDbDatabase::ltScale() const;

    设置默认线型缩放比例中的比例 Mcad::ErrorStatus McDbDatabase::setCeltscale(double);

    读取默认线型缩放比例中的比例double McDbDatabase::celtscale() const;


数据库层值


下面的函数可以用来设置和获取数据库当前的层值:


Mcad::ErrorStatus McDbDatabase::setClayer(McDbObjectId);
McDbObjectId McDbDatabase::clayer() const;
打开和关闭数据库对象

每一个数据库对象都可以通过下面3种方式来获得:


    通过对象句柄

    通过对象ID

    通过C++实例指针


当控件没有运行时,图形保存在文件系统中,而包含在DWG文件中的对象是通过它的句柄来识别的。


当图形打开后,图形信息可以通过McDbDatabase对象来访问。在数据库中每一个对象都有一个对象ID,这个对象ID保存在当前编辑过程中,从McDbDatabase的创建直到删除,对象ID一直存在。对象打开函数将对象ID作为参数,返回一个McDbObject对象指针。这个指针在对象被删除前一直有效。


可以使用Mx::mcdbOpenObject()函数来打开对象,该函数的原型如下:


Mcad::ErrorStatus mcdbOpenMcDbObject (
McDbObject*& obj, 
McDbObjectId id, 
McDb::OpenMode mode, 
Mdesk::Boolean openErasedObject = Mdesk::kFalse);
Mcad::ErrorStatus mcdbOpenObject(T_OBJECT*& pObj, McDbObjectId id, McDb::OpenMode mode, bool openErased = false);


可以使用下面的函数通过句柄获取对象ID,函数原型如下:


Mcad::ErrorStatus McDbDatabase::getAcDbObjectId(McDbObjectId& retId,Mdesk::Boolean createIfNotFound, const McDbHandle& objHandle, Mdesk::UInt32 xRefId=0);


可以使用McDbObjectPointer模板类打开:


McDbObjectPointer(McDbObjectId   objId,
                      McDb::OpenMode mode,
                      bool           openErased = false);


当然,也可以打开一个对象,然后获得此对象的句柄:


pObject->getMcDbHandle(handle);


mds_name与对象ID作用是相同的,在McDb库中提供了两个单独的函数在mds_name和McDbObjectId之间转换,函数原型如下:


mcdbGetAdsName(mds_name& objName, McDbObjectId objId);
mcdbGetObjectId(McDbObjectId& objId, mds_name objName);


可以使用如下方式打开对象:


    kForRead:读方式。如果对象没有被以写方式打开,它可以被最多256个使用者以读方式打开。

    kForWrite:写方式。如果一个对象没有被打开,则它可以被以写方式打开,否则打开对象的操作将失败。

删除对象

可以使用McDbObject::new()函数来创建一个McDbObject对象的实例并将它添加到数据库中。当对象刚被创建还没有添加到数据库中时,用户可以删除它,但是,一旦对象被添加到了数据库,用户就不能删除该对象,而由控件来管理数据库对象的删除工作。

删除数据库对象

数据库中的任何对象都可以使用下面的函数删除:


Mcad::ErrorStatus McDbObject::erase(Mdesk::Boolen Erasing = Mdesk::kTrue);


对数据库对象和实体来说,使用erase()函数将会有不同的结果。当数据库对象被擦除时,该对象的相关信息都被从字典中删除,如果使用erase(kFalse)恢复对象,相关信息不能自动恢复,必须使用setAt()函数将想关信息重新添加到字典中。当用户擦除一个实体时,它仅仅在块表记录中作了一个被擦除的标记,可以使用erase(kFalse)恢复对象。


    使用Mx::mcdbOpenObject和Mx::mcdbOpenMcDbObject打开后删除不可恢复。

    使用McDbObjectPointer智能指针打开后,删除可以恢复。

添加对象指定数据

用户可以在自己的应用程序中使用2种机制来添加对象指定数据,这2种机制如下:


    扩展数据(xdata)

    扩展记录


扩展数据


扩展数据是一个结果缓冲区链表,应用程序可以访问这个链表。数据是通过从1000~1071的DXF组码相关联的。这种机制可以节省空间,并且使得向对象添加数据变得很容易。但是扩展数据大小必须限制在16K以内,而且必须是属于DXF组码的数据类型。


可以使用McDbObject::xData()函数获得在对象中复制扩展数据的链表,该函数的原型如下:


virtual resbuf* McDbObject::xData(const char* regappname = NULL) const;


也可以使用McDbObject::setXData()函数来设置对象的扩展数据,该函数的原型如下:


virtual Mcad::ErrorStatus McDbObject::setXData(const resbuf* xdata);


setXData function


接口virtual Mcad::ErrorStatus setXData(const struct resbuf* xdata);


参数


名称说明
xdata

扩展数据链表指针,在不使用时调用Mx::mcutRelRb释放链表


返回值:如果成功返回Mcad::eOk。


参考例程


void CTestCommands::TestDataWrite()
 {
   MrxDbgSelSet mSelset;
   mSelset.userSelect();
   McDbObjectIdArray vId;
   mSelset.asArray(vId);
  
   ID = vId[0];
  
   McDbObjectPointer<McDbText> spText(ID, McDb::kForWrite);
   
  
   CString sAppName = _T("TestExData");
   int iValue = 999;
  
   acdbRegApp(sAppName);
   struct resbuf* pExDataRb = acutBuildList(1001, sAppName, AcDb::kDxfXdInteger16, iValue, 0);
   spText->setXData(pExDataRb);
 }


xData function


接口virtual struct resbuf*    xData   (LPCTSTR pszRegappName = NULL) const;


参数


名称说明
pszRegappName

扩展数据应用名,如果为空返回所有扩展数据


返回值:返回扩展数据链表,不需要是调用 Mx::mcutNewRb释放内链表。


参考例程


void CTestCommands::TestDataRead()
 {
   McDbObjectPointer<McDbText> spText(ID, McDb::kForWrite);
  
   struct resbuf* pExDataRb = spText->xData(_T(""));
  
   if (pExDataRb == NULL)
   {
    acutPrintf(_T("\n 没有扩展数据"));
   }
   else
   {
    CTestCommands::printResbufChain(pExDataRb);
   }
 }


扩展字典


每个对象都可以有一个扩展字典,它包含一个任意的McDbObject对象序列。

对象的关闭

对象打开后,当操作完成时,不在使用对象时,控件要求必须关闭对象,关闭对象调用McDbObject::close()函数,像这样调用: pObject->close();


    因为有这样的要求,便常常在使用控件过程中,很容易忘记关闭对象,建议使用智能指针的方式打开对象,而对象的关闭会在智能指针释放的时候自动调用。


智能针指的使用例如:

打开一个实体:


McDbObjectPointer<McDbEntity> spEnt(id,McDb::kForRead);
if(spEnt.openStatus() != Mcad::eOk)
continue;


打开当前数据库中的模型空间:


McDbBlockTableRecordPointer spBlkTabRec(MCDB_MODEL_SPACE,
	McDbCurDwg(),McDb::kForRead);
if(spBlkTabRec.openStatus() != Mcad::eOk)
{
	return retId;
}
实体对象

实体简介


实体是具有图形表示的数据库对象,实体的例子有直线、圆、圆弧、文本、曲线和椭圆等。

实体组成了控件图形,用户在控件中的操作都是对实体进行的,可见实体具有非常重要的地位。在控件中绝大部分实体都是从McDbEntity中派生出来的,而McDbEntity是从McDbObject中派生出来的。


实体的共同属性


所有实体都有一些共同的属性,这些属性可以通过相同的成员函数来获得或修改,当然,这些属性也可以被用户命令修改。实体的共同属性如下:


    颜色

    线型

    线型比例

    可见性

    线宽


当用户添加一个实体到块表记录中时,控件会自动将用户没有指定的实体属性设为缺省值。


颜色


实体的颜色可以通过0~256的数字值来指定和读取,也可以通过McCmColor类的实例来指定和读取,McCmColor类可以用来扩展颜色模型。McCmColor类可以用来表示真彩色中的任何一种色彩。


颜色值1~7代表的标准颜色如下:


颜色值1234567
颜色绿紫红白或黑


颜色值7对应的颜色与控件背景颜色有关。如果背景是黑,则7对应的颜色是白,反之则对应的颜色是黑。


还有一些颜色值代表了一些特殊的含义:


    0:指定实体的颜色为BYBLOCK。即实体继承当前块表记录中块参照的颜色值,如果实体直接处于模型空间或图纸空间块表记录中,则实体的颜色就是黑或白。

    256:指定实体的颜色为BYLAYER。实体使用其所在层的颜色。

    257:实体无颜色。这种情况仅在实体刚刚实例化,在被赋予0~256之间颜色值或被添加到数据库并使用数据库的当前颜色值之前才会出现。


可以使用下面的函数来设置或查询实体的颜色:


通过索引设置颜色:


Mcad::ErrorStatus McDbEntity::setColorIndex(Mdesk::Uint16 color);


通过索引读取颜色:


Mdesk::Uint16 McDbEntity::colorIndex() const;


通过McCmColor类设置颜色:


Mcad::ErrorStatus McDbEntity::setColor(const McCmColor& color, Mdesk::BooleandoSubents = true);


通过McCmColor类读取颜色:


McCmColor McDbEntity::color() const;


线型


当一个实体被创建时,它的线型被设置为NULL。当实体被添加到数据库后,如果没有这个实体指定的线型,则该实体的线型将被设置成数据库的当前线型值。这个缺省的线型值存放在CELTYPE系统变量中。线型可以通过名称字符串或在实体所在数据库的McDbLineTypeTableRecord的对象ID来指定。


可以使用下面的函数用实体名或对象ID来指定实体的线型:


使用函数用实体名来指定实体的线型:


Mcad::ErrorStatus McDbEntity::setLinetype(const char* newVal);


使用对象ID来指定实体的线型:


Mcad::ErrorStatus McDbEntity::setLinetype(McDbObjectId newVal);


下面的函数返回实体的线型名:


LPCTSTR  McDbEntity::linetype() const;


下面的函数返回线型符号表记录的对象ID:


McDbObjectId McDbEntity::linetypeId() const;


线型比例


当一个实体被创建时,它的线型比例被设置成一个无效值。当对象被添加到数据库中后,如果没有为这个实体指定线型比例,则该实体的线型比例将被设置成数据库的当前线型比例值。这个缺省的线型比例值存放在CELTSCALE系统变量中。


可以使用下面的函数来设置或查询一个实体的线型比例值:


可以使用下面的函数来设置一个实体的线型比例值:


Mcad::ErrorStatus McDbEntity::setLinetypeScale(double newVal);


可以使用下面的函数来查询一个实体的线型比例值:


Double McDbEntity::linetypeScale() const;


    当实体被重新生成后,它的有效线型比例是实体自身线型比例和全局数据库线型比例的乘积。对于非图纸空间的实体,它的线型比例可按如下方法计算:

    effltscale = ent->linetypeScale() * ent->database()->ltscale();


可见性


如果指定一个实体不可见,则不管数据库中其他设置如何,该实体都将不可见。其他因素也可能导致实体不可见,例如:如果实体所在层被关闭或冻结,则该实体不可见。实体可见或不可见可以用一个变量McDb::Visibility来表示,它的值可以是kInvisiblekVisible


可以用下面的函数来设置或查询一个实体的可见性:


可以用下面的函数来设置一个实体的可见性:


Mcad::ErrorStatus McDbEntity::setVisibility(McDb::Visibility newVal);


可以用下面的函数来查询一个实体的可见性:


McDb::Visibility McDbEntity::visibility() const;



所有实体都在某一个层上。在数据库中,最少包含一个层(初始情况下是0层)。和线型一样,用户可以为一个实体指定所在的层。如果用户不为实体指定层,则系统将会把这个实体添加到缺省层上。


    每一个层有自己相关的属性,这此属性包括冻结/解冻、打开/关闭、锁定/解锁、颜色、线型和视口。当一个实体的颜色或线型是BYLAYER时,实体上的相关属性将会使用层的属性。


可以使用下面的函数来设置或查询一个实体所在的层:


可以使用下面的函数来设置一个实体所在的层:


Mcad::ErrorStatus McDbEntity::setLayer(const char* newVal);


可以使用下面的函数来查询一个实体所在的层:


Mcad::ErrorStatus McDbEntity::setLayer(McDbObjectId newVal);


下面的函数返回当前实体所在层的名称:


LPCTSTR McDbEntity::layer() const;


下面的函数返回实体所在层的对象ID:


McDbObjectId McDbEntity::layerId() const;
容器对象

容器对象在图形文件初始化时,就已经存在了。容器对象包括符号表、字典、组和扩展记录。下面介绍如何向符号表、字典和组中添加实体,怎样使用遍历来查询容器的内容,以及怎样创建和使用自己的字典和扩展记录来管理应用程序数据和对象。


符号表


符号表具有这样的功能:它可以包含数据库对象,这些数据库对象能够通过一个字符串关键字来搜索。用户可以向这些容器中添加实体,也可以使用遍历来浏览容器以查找所需的内容。


符号表和字典的异同


符号表和字典在本质上有相同的功能。字典为用户提供了和符号表类似的机制来保存和恢复带有相关关键字的对象。当创建一个新的图形时,它就创建了一个命名对象字典,这个字典可以看作是图形中实体对象的容器列表。用户可以创建任意数量的对象并将它们添加到命名对象字典中。


    符号表和字典的一个重要区别是符号表记录不能由ObjectARX应用程序直接删除,而一个字典所包含的对象则可以直接被删除。


符号表介绍


在符号表记录和字典中使用的名称必须遵循以下规则:


    1. 名称可以有任意长度,但是在控件中用户输入的符号名不能超过255个字符。

    2. 保留了使用名称中的大小写的权力,但是并没有在比较中区分大小写。

    3. 除了逗号、单引号、分号、和等号之外,名称可以由任何Windows NT文件名中可以使用的字符组成。数据库中包含以下符号表:


    类型类名
    块表McDbBlockTable; BLOCK
    层表McDbLayerTable: LAYER
    文本样式表McDbTextStyleTable; STYLE
    线型表McDbLinetypeTable: LTYPE


每一个表都包含相应的McDbSymbolTableRecord类的子类。

每一个符号表都提供了一个getAt()函数来查找指定名称的记录。另外还有has()add()函数,前者可以用来判断一个记录名是否已经包含在符号表中,后者用来向符号表中添加一个新记录。


块表


在数据库中的实体通常属于一个块表记录。缺省情况下,块表包含1个记录:*MODEL_SPACE


层表


在缺省情况下,层表中包含0层一个层。下面是一些常用的设置和查询层特性信息的函数:


冻结和解冻:当一个层被冻结时,该层的图形将不能重新生成。


void McDbLayerTableRecord::setIsFrozen(Mdesk::Boolean);
Mdesk::Boolean McDbLayerTableRecord::isFrozen() const;


打开和关闭:当一个图层被关闭时,该层的图形将不再显示。


void McDbLayerTableRecord::setIsOff(Mdesk::Boolean);
Mdesk::Boolean McDbLayerTableRecord::isOff() const;


锁定和解锁:在控件中,用户不能修改在一个锁定层上的实体,也不能在应用程序中以write()函数打开实体。


void McDbLayerTableRecord::setIsLocked(Mdesk::Boolean);
Mdesk::Boolean McDbLayerTableRecord::isLocked() const;


颜色:当一个实体的颜色被设置成BYLAYER时,可以通过setColor()函数来设置实体的颜色。


void McDbLayerTableRecord::setColor(const AcCmColor &color);
AcCmColor McDbLayerTableRecord::color() const;


线型:当一个实体的线型是BYLAYER时,可以通过setLinetypeObjedtId()函数来设置实体的线型。


void McDbLayerTableRecord::setLinetypeObjectId(McDbObjectId);
McDbObjectId McDbLayerTableRecord::linetypeObjectId() const;


遍历器


每一个符号表都有一个相应的遍历器,用户可以使用newIterator()函数创建新的遍历器对象,用来浏览整个表内的所有对象。在使用完这个遍历器之后,一定要删除它。另外,块表中还有一个可以用来操作其实体的遍历器。


字典


要创建一个新的字典,用户需要创建一个McDbDictionary的实体,将这个实例添加到数据库中并在它的主对象中注册。可以使用McDbDictionary类的setAt()函数来将对象添加到字典和数据库中。

扩展记录

扩展记录允许用户添加应用程序指定的辅助数据。因为在定义用户自己的类时可以选择扩展数据。一个扩展记录是一个McDbxrecord类的实例,而McDbxrecord类是从McDbObject类派生的。扩展数据的声明是由一个结果缓冲区的形式定义的,它是一个数据列表,其中每一项包含了一个DXF组码和相应的数据。下面将会介绍DXF组码的含义。


对于用户保存在一个扩展记录中的数据的量原则上是没有限制的,扩展记录可以从属于任何其他对象,包括扩展字典的任何对象、命名对象字典、其他字典或其他扩展记录。


McDbxrecord类提供了两个函数用来设定和获取扩展记录:


设定扩展记录:


Mcad::ErrorStatus McDbXrecord::setFromRbChain(resbuf& pRb, McDbDatabase* auxDb = NULL);


获取扩展记录:


Mcad::ErrorStatus McDbXrecord::rbChain(resbuf** ppRb, McDbDatabase* auxDb = NULL) const;
MxDraw
MxDraw是由梦想凯德基于AutoDesk CAD平台开发的软件,拥有完全自主的核心技术和知识产权。MxDraw致力于为各企业提供最优秀的CAD平台整体解决方案。
技术服务
TEL:400-888-5703
185-8173-1060
QQ:827867134,6884123
产品购买
TEL:400-888-5703
185-8173-1060
QQ:827867134,6884123
用户交流
QQ群1:827867134
QQ群2:827867134
QQ群3:827867134