起步软件技术论坛-X3

 找回密码
 立即注册
搜索
查看: 278|回复: 2

【分享】如何在X3上扩展开发移动办公系统、让手机支持X3业务流程的流转控制处理?****

[复制链接]
发表于 2009-7-7 16:21:41 | 显示全部楼层 |阅读模式
近来根据企业的发展需要,在X3平台上开发移动办公系统,其中涉及到X3本身的业务流程处理。
   这个系统的开发,瓶颈在于X3没有对平台外提供流程引擎的接口调用,导致在平台外完全无法控制流程处理。所以,关键点集中在如何绕过 Justep X3 流程引擎,直接控制流程流转的问题上。

    实际上,X3平台的流程引擎内核并不复杂,存储上涉及到的表仅5张,表结构也容易理解,抽象得不错。具体为:流程实例表(TFlow)、流程ID表(TFlowID)、任务表(TTask)、任务消息表(TTaskMessage)和任务业务数据表(TTaskBizData)。
    我的解决思路,就是直接存取访问这5涨表,根据必要的参数,模拟平台流程引擎中基本的流程控制流转方法,向这5张表中写入相应的数据,实现和X3平台本身兼容的流程控制。
    目前实现了启动流程、流程向下流转、流程回退、流程中止、流程结束功能。标准SQL语句实现,没有使用平台流程引擎的任何接口。
回复

使用道具 举报

 楼主| 发表于 2009-7-7 16:43:17 | 显示全部楼层

流程控制相应方法,具体实现代码封装为 TFlowControlService 类

{ TFlowControlService }

  TFlowControlService = class
  public
    { 启动流程 }
    static procedure StartupFlow(AProcURL, AEntryUnitID, AFlowID, AFlowSubject,
      ASenderURL, AReceiverURL, ATaskSubject: string); overload;
    static procedure StartupFlow(AProcURL, AEntryUnitID, AFlowID, AFlowSubject,
      ATaskSubject, ASenderURL: string; AReceiverURLs: TStrings;
      AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode); overload;

    { 流程向下流转 }
    static procedure FlowOut(ACurrentTaskGUID, ANextProcUnitID, ANextProcUnitName,
      ATaskSubject, ATaskFuncURL: string; AReceiverURLs: TStrings;
      AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode); overload;
    static procedure FlowOut(ACurrentTaskGUID, ANextProcUnitID, ANextProcUnitName,
      ATaskSubject, ATaskFuncURL, AReceiverURL: string); overload;

    { 流程回退 }
    static procedure FlowBack(ACurrentTaskGUID, APrevProcUnitID, APrevProcUnitName,
      ATaskSubject, ATaskFuncURL: string; AReceiverURLs: TStrings;
      AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode); overload;
    static procedure FlowBack(ACurrentTaskGUID, APrevProcUnitID, APrevProcUnitName,
      ATaskSubject, ATaskFuncURL, AReceiverURL: string); overload;

    { 流程中止 }
    static procedure FlowAbort(ACurrentTaskGUID: string);

    { 流程结束 }
    static procedure FlowFinish(ACurrentTaskGUID: string);
  end;


{ TFlowControlService }

static procedure TFlowControlService.StartupFlow(AProcURL, AEntryUnitID, AFlowID, AFlowSubject,
  ASenderURL, AReceiverURL, ATaskSubject: string);
var
  lReceiverURLs: TStrings;
begin
  lReceiverURLs := TStringList.Create;
  try
    lReceiverURLs.Add(AReceiverURL);
    TFlowControlService.StartupFlow(AProcURL, AEntryUnitID, AFlowID, AFlowSubject,
      ATaskSubject, ASenderURL, lReceiverURLs, TTaskExecuteMode.emExclusive,
      TTaskPreemptMode.omFirstProcess);
  finally
    lReceiverURLs.Free;
  end;
end;

static procedure TFlowControlService.StartupFlow(AProcURL, AEntryUnitID, AFlowID,
  AFlowSubject, ATaskSubject, ASenderURL: string; AReceiverURLs: TStrings;
  AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode);
const
  c_Insert_TFlow = 'insert into TFLOW(FGUID, FID, FSUBJECT, FCREATETIME, FSTATE, FPROCURL, FFLOWFIELDURL) values(:GUID, :FlowID, :Subject, :CreateTime, :State, rocURL, :FlowFieldURL)';
  c_Insert_TFlowID = 'insert into TFLOWID values(:FlowGUID, :FlowID)';
  c_Insert_TTask = 'insert into TTASK(FGUID, FFLOWGUID, FPARENTGUID, FPREVGUID, FNEXTGUID, FGROUPGUID, FPROCURL, FPROCUNITID, FSUBJECT, FKIND, FSOGN, FSOGNNAME, FSDEPT, FSDEPTNAME, FSPOSITION, FSPOSITIONNAME, FSPERSON, FSPERSONNAME, FSORGURL, FPRIORITY, FCREATETIME, FSTARTTIME, FDESCRIPTION, FSTATE, FFUNCURL, FTYPENAME, FEXECUTEMODE, FPREEMPTMODE, FLASTCHANGETIME, FNEEDPROCESS, FRPERSONNAMES) ' +
    'values(:GUID, :FlowGUID, arentGUID, REVGUID, :NextGUID, :GroupGUID, rocURL, rocUnitID, :Subject, :Kind, :SOGN, :SOGNNAME, :SDept, :SDeptName, :SPosition, :SPositionName, :SPerson, :SPersonName, :SOrgURL, riority, :CreateTime, :StartTime, escription, :State, :FuncURL, :TypeName, :ExecuteMode, reemptMode, astChangeTime, :NeedProcess, :RPersonNames)';
  c_Insert_TTaskMessage = 'insert into TTASKMESSAGE(FGUID, FPARENTGUID, FTASKGUID, FROGN, FROGNNAME, FRDEPT, FRDEPTNAME, FRPOSITION, FRPOSITIONNAME, FRPERSON, FRPERSONNAME, FRORGURL, FRECEIVETIME, FSTATE) ' +
    'values(:GUID, ARENTGUID, :TASKGUID, :ROGN, :ROGNNAME, :RDEPT, :RDEPTNAME, :RPOSITION, :RPOSITIONNAME, :RPERSON, :RPERSONNAME, :RORGURL, :RECEIVETIME, :STATE)';
  c_Insert_TTaskBizData = 'insert into TTASKBIZDATA(FGUID, FTASKGUID, FKEY0, FKEYVALUE0) ' +
    'values(:GUID, :TaskGUID, :Key0, :KeyValue0)';
var
  lProcURL: TBizURL;
  lProc: TProc;
  lEntryUnit: TProcUnit;
  lFlowFieldURL, lTaskFuncURL: string;
  lFlowGUID, lTaskGUID, lTaskMessageGUID, lTaskBizDataGUID: string;
  lQuery: TQuery;
  lTransactionHandle: TTransactionHandle;
  I: integer;
  lReceiverURL: string;
  lSenderOrgan, lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
    lSenderPositionName, lSender, lSenderName: string;
  lReceiverOrgan, lReceiverOrganName, lReceiverDept, lReceiverDeptName,
    lReceiverPosition, lReceiverPositionName, lReceiver, lReceiverName: string;
  lReceiverPersonNames: string;
begin
  { 一、从流程获取所需信息、变量赋值 }
  lProcURL := TBizURL.Create;
  try
    lProcURL.URL := AProcURL;
    lProc := BizSys.BizSystem.GetBizObject(lProcURL) as TProc;
    lFlowFieldURL := lProc.FlowField.URL;
    lEntryUnit := lProc.GetUnit(AEntryUnitID);
    lTaskFuncURL := (lEntryUnit as TProcActivity).FuncURL.URL;
  finally
    lProcURL.Free;
  end;

  TOrgUtils.GetOrganDeptPositionPersonIDName(ASenderURL, lSenderOrgan,
    lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
    lSenderPositionName, lSender, lSenderName);
  lReceiverPersonNames := TOrgUtils.GetOrgURLsDisplayName(AReceiverURLs, -1);

  lFlowGUID := jsCommon.CreateGUIDStr;
  lTaskGUID := jsCommon.CreateGUIDStr;

  { 二、生成流程任务数据到数据库相关表 }
  lQuery := TQuery.Create(nil);
  try
    lQuery.ConnectionString := BizObjConsts.cSysDatabaseConnectionString;
    lTransactionHandle := lQuery.Connection.Transaction.Start(True); //启动事务
    try
      { 1、TFlow }
      lQuery.CommandText := c_Insert_TFlow;
      lQuery.Params.ParamByName('GUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('FlowID').AsBlob := AFlowID;
      lQuery.Params.ParamByName('Subject').AsString := AFlowSubject;
      lQuery.Params.ParamByName('CreateTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('State').AsString := Flow.FlowStateToStr(TFlowState.fsProcessing);
      lQuery.Params.ParamByName('ProcURL').AsString := AProcURL;
      lQuery.Params.ParamByName('FlowFieldURL').AsString := lFlowFieldURL;
      lQuery.Execute;

      { 2、TFlowID }
      lQuery.CommandText := c_Insert_TFlowID;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('FlowID').AsString := AFlowID;
      lQuery.Execute;

      { 3、TTask }
      lQuery.CommandText := c_Insert_TTask;
      lQuery.Params.ParamByName('GUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('ParentGUID').AsString := '-1';
      lQuery.Params.ParamByName('PREVGUID').AsString := '-1';
      lQuery.Params.ParamByName('NextGUID').AsString := '-1';
      lQuery.Params.ParamByName('GroupGUID').AsString := '-1';
      lQuery.Params.ParamByName('ProcURL').AsString := AProcURL;
      lQuery.Params.ParamByName('ProcUnitID').AsString := AEntryUnitID;
      lQuery.Params.ParamByName('Subject').AsString := ATaskSubject;
      lQuery.Params.ParamByName('Kind').AsString := Flow.FlowTaskKindToStr(TFlowTaskKind.ftkActivity);// 'ftkActivity';
      lQuery.Params.ParamByName('SOGN').AsString := lSenderOrgan;
      lQuery.Params.ParamByName('SOGNNAME').AsString := lSenderOrganName;
      lQuery.Params.ParamByName('SDept').AsString := lSenderDept;
      lQuery.Params.ParamByName('SDeptName').AsString := lSenderDeptName;
      lQuery.Params.ParamByName('SPosition').AsString := lSenderPosition;
      lQuery.Params.ParamByName('SPositionName').AsString := lSenderPositionName;
      lQuery.Params.ParamByName('SPerson').AsString := lSender;
      lQuery.Params.ParamByName('SPersonName').AsString := lSenderName;
      lQuery.Params.ParamByName('SOrgURL').AsString := FileSys.FileUtils.RemoveFilePrefix(ASenderURL);
      lQuery.Params.ParamByName('Priority').AsString := 'tpNormal';
      lQuery.Params.ParamByName('CreateTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('StartTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('Description').AsString := '';
      lQuery.Params.ParamByName('State').AsString := Task.TaskStateToStr(TTaskState.tsStarted);
      lQuery.Params.ParamByName('FuncURL').AsString := lTaskFuncURL;
      lQuery.Params.ParamByName('TypeName').AsString := lEntryUnit.DisplayName;
      lQuery.Params.ParamByName('ExecuteMode').AsString := TFlowTaskUtils.TaskExecuteModeToStr(AExecuteMode);
      lQuery.Params.ParamByName('PreemptMode').AsString :=  TFlowTaskUtils.TaskPreemptModeToStr(APreemptMode);
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('NeedProcess').AsString := 'Y';
      lQuery.Params.ParamByName('RPersonNames').AsString := lReceiverPersonNames;
      lQuery.Execute;

      { 4、TTaskMessage }
      for I := 0 to AReceiverURLs.Count - 1 do
      begin
        lReceiverURL := AReceiverURLs[I];
        TOrgUtils.GetOrganDeptPositionPersonIDName(lReceiverURL, lReceiverOrgan,
          lReceiverOrganName, lReceiverDept, lReceiverDeptName, lReceiverPosition,
          lReceiverPositionName, lReceiver, lReceiverName);

        lTaskMessageGUID := jsCommon.CreateGUIDStr;

        lQuery.CommandText := c_Insert_TTaskMessage;
        lQuery.Params.ParamByName('GUID').AsString := lTaskMessageGUID;
        lQuery.Params.ParamByName('PARENTGUID').AsString := '-1';
        lQuery.Params.ParamByName('TASKGUID').AsString := lTaskGUID;
        lQuery.Params.ParamByName('ROGN').AsString := lReceiverOrgan;
        lQuery.Params.ParamByName('ROGNNAME').AsString := lReceiverOrganName;
        lQuery.Params.ParamByName('RDEPT').AsString := lReceiverDept;
        lQuery.Params.ParamByName('RDEPTNAME').AsString := lReceiverDeptName;
        lQuery.Params.ParamByName('RPOSITION').AsString := lReceiverPosition;
        lQuery.Params.ParamByName('RPOSITIONNAME').AsString := lReceiverPositionName;
        lQuery.Params.ParamByName('RPERSON').AsString := lReceiver;
        lQuery.Params.ParamByName('RPERSONNAME').AsString := lReceiverName;
        lQuery.Params.ParamByName('RORGURL').AsString := FileSys.FileUtils.RemoveFilePrefix(lReceiverURL);
        lQuery.Params.ParamByName('RECEIVETIME').AsDateTime := SysSrv.SysService.Time;
        lQuery.Params.ParamByName('STATE').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsReceived);
        lQuery.Execute;
      end;

      { 5、TTaskBizData }
      lTaskBizDataGUID := jsCommon.CreateGUIDStr;
      lQuery.CommandText := c_Insert_TTaskBizData;
      lQuery.Params.ParamByName('GUID').AsString := lTaskBizDataGUID;
      lQuery.Params.ParamByName('TaskGUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('Key0').AsString := lFlowFieldURL;
      lQuery.Params.ParamByName('KeyValue0').AsString := AFlowID;
      lQuery.Execute;

      lQuery.Connection.Transaction.Commit(lTransactionHandle);  //提交事务
    except
      lQuery.Connection.Transaction.Rollback(lTransactionHandle); //回滚事务
      raise;
    end;
  finally
    lQuery.Free;
  end;
end;

static procedure TFlowControlService.FlowOut(ACurrentTaskGUID, ANextProcUnitID,
  ANextProcUnitName, ATaskSubject, ATaskFuncURL: string; AReceiverURLs: TStrings;
  AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode);
const
  c_Select_TTask = 'select * from TTASK where FGUID = :TaskGUID';
  c_Select_TTaskMessage = 'select * from TTASKMESSAGE where FTASKGUID = :TaskGUID';
  c_Select_TFlow = 'select * from TFLOW where FGUID = :FlowGUID';
  c_Select_TFlowID = 'select * from TFLOWID where FFLOWGUID = :FlowGUID';

  c_Update_TTask = 'update TTASK set FNEXTGUID = null, FSTATE = :TaskState, FFINISHTIME = :FinishTime, FLASTCHANGETIME = astChangeTime where FGUID = :TaskGUID';
  c_Update_TTaskMessage = 'update TTASKMESSAGE set FSTATE = :TaskMeesageState, FFINISHTIME = :FinishTime where FTASKGUID = :TaskGUID';

  c_Insert_TTask = 'insert into TTASK(FGUID, FFLOWGUID, FPARENTGUID, FPREVGUID, FNEXTGUID, FGROUPGUID, FPROCURL, FPROCUNITID, FSUBJECT, FKIND, FSOGN, FSOGNNAME, FSDEPT, FSDEPTNAME, FSPOSITION, FSPOSITIONNAME, FSPERSON, FSPERSONNAME, FSORGURL, FPRIORITY, FCREATETIME, FSTARTTIME, FDESCRIPTION, FSTATE, FFUNCURL, FTYPENAME, FEXECUTEMODE, FPREEMPTMODE, FLASTCHANGETIME, FNEEDPROCESS, FRPERSONNAMES) ' +
    'values(:GUID, :FlowGUID, arentGUID, REVGUID, :NextGUID, :GroupGUID, :ProcURL, :ProcUnitID, :Subject, :Kind, :SOGN, :SOGNNAME, :SDept, :SDeptName, :SPosition, :SPositionName, :SPerson, :SPersonName, :SOrgURL, :Priority, :CreateTime, :StartTime, escription, :State, :FuncURL, :TypeName, :ExecuteMode, :PreemptMode, astChangeTime, :NeedProcess, :RPersonNames)';
  c_Insert_TTaskMessage = 'insert into TTASKMESSAGE(FGUID, FPARENTGUID, FTASKGUID, FROGN, FROGNNAME, FRDEPT, FRDEPTNAME, FRPOSITION, FRPOSITIONNAME, FRPERSON, FRPERSONNAME, FRORGURL, FRECEIVETIME, FSTATE) ' +
    'values(:GUID, :PARENTGUID, :TASKGUID, :ROGN, :ROGNNAME, :RDEPT, :RDEPTNAME, :RPOSITION, :RPOSITIONNAME, :RPERSON, :RPERSONNAME, :RORGURL, :RECEIVETIME, :STATE)';
  c_Insert_TTaskBizData = 'insert into TTASKBIZDATA(FGUID, FTASKGUID, FKEY0, FKEYVALUE0) ' +
    'values(:GUID, :TaskGUID, :Key0, :KeyValue0)';
var
  lQuery: TQuery;
  lTransactionHandle: TTransactionHandle;
  lProcURL, lFlowFieldURL, lFlowID: string;
  lFlowGUID, lTaskGUID, lTaskMessageGUID, lTaskBizDataGUID: string;
  lSenderURL, lReceiverURL: string;
  lSenderOrgan, lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
    lSenderPositionName, lSender, lSenderName: string;
  lReceiverOrgan, lReceiverOrganName, lReceiverDept, lReceiverDeptName,
    lReceiverPosition, lReceiverPositionName, lReceiver, lReceiverName: string;
  lReceiverPersonNames: string;
  I: integer;
begin
  lQuery := TQuery.Create(nil);
  try
    lQuery.ConnectionString := BizObjConsts.cSysDatabaseConnectionString;

    { 一、根据当前任务获取所需任务信息、变量赋值 }
    { 1、任务表 }
    lQuery.CommandText := c_Select_TTask;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的任务。');
    lFlowGUID := lQuery.FieldByName('FFlowGUID').AsString;
    lQuery.Close;
    { 2、任务消息表 }
    lQuery.CommandText := c_Select_TTaskMessage;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的任务消息。');
    lSenderURL := 'Org:' + lQuery.FieldByName('FRORGURL').AsString;
    lQuery.Close;
    { 3、流程表 }
    lQuery.CommandText := c_Select_TFlow;
    lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的流程。');
    lProcURL := lQuery.FieldByName('FPROCURL').AsString;
    lFlowFieldURL := lQuery.FieldByName('FFLOWFIELDURL').AsString;
    lQuery.Close;
    { 4、流程ID表 }
    lQuery.CommandText := c_Select_TFlowID;
    lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的流程ID记录。');
    lFlowID := lQuery.FieldByName('FID').AsString;
    lQuery.Close;



    lTransactionHandle := lQuery.Connection.Transaction.Start(True); //启动事务
    try
      { 二、更新当前流程任务 }

      { 1、Update TTask }
      lQuery.CommandText := c_Update_TTask;
      lQuery.Params.ParamByName('TaskState').AsString := Task.TaskStateToStr(TTaskState.tsFinished);
      lQuery.Params.ParamByName('FinishTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Execute;
      { 2、Update TTaskMessage }
      lQuery.CommandText := c_Update_TTaskMessage;
      lQuery.Params.ParamByName('TaskMeesageState').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsFinished);
      lQuery.Params.ParamByName('FinishTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Execute;



      { 三、生成下一步流程任务数据到数据库相关表 }

      { 1、Add To TTask }
      TOrgUtils.GetOrganDeptPositionPersonIDName(lSenderURL, lSenderOrgan,
        lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
        lSenderPositionName, lSender, lSenderName);
      lReceiverPersonNames := TOrgUtils.GetOrgURLsDisplayName(AReceiverURLs, -1);
      lTaskGUID := jsCommon.CreateGUIDStr;

      lQuery.CommandText := c_Insert_TTask;
      lQuery.Params.ParamByName('GUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('ParentGUID').AsString := '-1';
      lQuery.Params.ParamByName('PREVGUID').AsString := ACurrentTaskGUID; //新建任务的上一任务关联到当前任务
      lQuery.Params.ParamByName('NextGUID').AsString := '-1';
      lQuery.Params.ParamByName('GroupGUID').AsString := '-1';
      lQuery.Params.ParamByName('ProcURL').AsString := SysUtils.UpperCase(lProcURL);
      lQuery.Params.ParamByName('ProcUnitID').AsString := ANextProcUnitID;
      lQuery.Params.ParamByName('Subject').AsString := ATaskSubject;
      lQuery.Params.ParamByName('Kind').AsString := Flow.FlowTaskKindToStr(TFlowTaskKind.ftkActivity);  //'ftkActivity';
      lQuery.Params.ParamByName('SOGN').AsString := lSenderOrgan;
      lQuery.Params.ParamByName('SOGNNAME').AsString := lSenderOrganName;
      lQuery.Params.ParamByName('SDept').AsString := lSenderDept;
      lQuery.Params.ParamByName('SDeptName').AsString := lSenderDeptName;
      lQuery.Params.ParamByName('SPosition').AsString := lSenderPosition;
      lQuery.Params.ParamByName('SPositionName').AsString := lSenderPositionName;
      lQuery.Params.ParamByName('SPerson').AsString := FileSys.FileUtils.RemoveFilePrefix(lSender);
      lQuery.Params.ParamByName('SPersonName').AsString := lSenderName;
      lQuery.Params.ParamByName('SOrgURL').AsString := FileSys.FileUtils.RemoveFilePrefix(lSenderURL);
      lQuery.Params.ParamByName('Priority').AsString := 'tpNormal';
      lQuery.Params.ParamByName('CreateTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('StartTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('Description').AsString := '';
      lQuery.Params.ParamByName('State').AsString := Task.TaskStateToStr(TTaskState.tsStarted);
      lQuery.Params.ParamByName('FuncURL').AsString := ATaskFuncURL;
      lQuery.Params.ParamByName('TypeName').AsString := ANextProcUnitName;
      lQuery.Params.ParamByName('ExecuteMode').AsString := TFlowTaskUtils.TaskExecuteModeToStr(AExecuteMode);
      lQuery.Params.ParamByName('PreemptMode').AsString :=  TFlowTaskUtils.TaskPreemptModeToStr(APreemptMode);
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('NeedProcess').AsString := 'Y';
      lQuery.Params.ParamByName('RPersonNames').AsString := lReceiverPersonNames;
      lQuery.Execute;

      { 2、Add TTaskMessage }
      for I := 0 to AReceiverURLs.Count - 1 do
      begin
        lReceiverURL := AReceiverURLs[I];
        TOrgUtils.GetOrganDeptPositionPersonIDName(lReceiverURL, lReceiverOrgan,
          lReceiverOrganName, lReceiverDept, lReceiverDeptName, lReceiverPosition,
          lReceiverPositionName, lReceiver, lReceiverName);

        lTaskMessageGUID := jsCommon.CreateGUIDStr;

        lQuery.CommandText := c_Insert_TTaskMessage;
        lQuery.Params.ParamByName('GUID').AsString := lTaskMessageGUID;
        lQuery.Params.ParamByName('PARENTGUID').AsString := '-1';
        lQuery.Params.ParamByName('TASKGUID').AsString := lTaskGUID;
        lQuery.Params.ParamByName('ROGN').AsString := lReceiverOrgan;
        lQuery.Params.ParamByName('ROGNNAME').AsString := lReceiverOrganName;
        lQuery.Params.ParamByName('RDEPT').AsString := lReceiverDept;
        lQuery.Params.ParamByName('RDEPTNAME').AsString := lReceiverDeptName;
        lQuery.Params.ParamByName('RPOSITION').AsString := lReceiverPosition;
        lQuery.Params.ParamByName('RPOSITIONNAME').AsString := lReceiverPositionName;
        lQuery.Params.ParamByName('RPERSON').AsString := lReceiver;
        lQuery.Params.ParamByName('RPERSONNAME').AsString := lReceiverName;
        lQuery.Params.ParamByName('RORGURL').AsString := FileSys.FileUtils.RemoveFilePrefix(lReceiverURL);
        lQuery.Params.ParamByName('RECEIVETIME').AsDateTime := SysSrv.SysService.Time;
        lQuery.Params.ParamByName('STATE').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsReceived);
        lQuery.Execute;
      end;

      { 3、Add TTaskBizData }
      lTaskBizDataGUID := jsCommon.CreateGUIDStr;
      lQuery.CommandText := c_Insert_TTaskBizData;
      lQuery.Params.ParamByName('GUID').AsString := lTaskBizDataGUID;
      lQuery.Params.ParamByName('TaskGUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('Key0').AsString := lFlowFieldURL;
      lQuery.Params.ParamByName('KeyValue0').AsString := lFlowID;
      lQuery.Execute;

      lQuery.Connection.Transaction.Commit(lTransactionHandle);  //提交事务
    except
      lQuery.Connection.Transaction.Rollback(lTransactionHandle); //回滚事务
      raise;
    end;
  finally
    lQuery.Free;
  end;
end;

static procedure TFlowControlService.FlowOut(ACurrentTaskGUID, ANextProcUnitID, ANextProcUnitName,
  ATaskSubject, ATaskFuncURL, AReceiverURL: string);
var
  lReceiverURLs: TStrings;
begin
  lReceiverURLs := TStringList.Create;
  try
    lReceiverURLs.Add(AReceiverURL);
    TFlowControlService.FlowOut(ACurrentTaskGUID, ANextProcUnitID, ANextProcUnitName,
      ATaskSubject, ATaskFuncURL, lReceiverURLs, TTaskExecuteMode.emExclusive,
      TTaskPreemptMode.omFirstProcess);
  finally
    lReceiverURLs.Free;
  end;
end;

static procedure TFlowControlService.FlowBack(ACurrentTaskGUID, APrevProcUnitID, APrevProcUnitName,
  ATaskSubject, ATaskFuncURL: string; AReceiverURLs: TStrings;
  AExecuteMode: TTaskExecuteMode; APreemptMode: TTaskPreemptMode);
const
  c_Select_TTask = 'select * from TTASK where FGUID = :TaskGUID';
  c_Select_TTaskMessage = 'select * from TTASKMESSAGE where FTASKGUID = :TaskGUID';
  c_Select_TFlow = 'select * from TFLOW where FGUID = :FlowGUID';
  c_Select_TFlowID = 'select * from TFLOWID where FFLOWGUID = :FlowGUID';

  c_Update_TTask = 'update TTASK set FNEXTGUID = null, FSTATE = :TaskState, FFINISHTIME = :FinishTime, FLASTCHANGETIME = astChangeTime where FGUID = :TaskGUID';
  c_Update_TTaskMessage = 'update TTASKMESSAGE set FSTATE = :TaskMeesageState, FFINISHTIME = :FinishTime where FTASKGUID = :TaskGUID';

  c_Insert_TTask = 'insert into TTASK(FGUID, FFLOWGUID, FPARENTGUID, FPREVGUID, FNEXTGUID, FGROUPGUID, FPROCURL, FPROCUNITID, FSUBJECT, FKIND, FSOGN, FSOGNNAME, FSDEPT, FSDEPTNAME, FSPOSITION, FSPOSITIONNAME, FSPERSON, FSPERSONNAME, FSORGURL, FPRIORITY, FCREATETIME, FSTARTTIME, FDESCRIPTION, FSTATE, FFUNCURL, FTYPENAME, FEXECUTEMODE, FPREEMPTMODE, FLASTCHANGETIME, FNEEDPROCESS, FRPERSONNAMES) ' +
    'values(:GUID, :FlowGUID, :ParentGUID, :PREVGUID, :NextGUID, :GroupGUID, :ProcURL, :ProcUnitID, :Subject, :Kind, :SOGN, :SOGNNAME, :SDept, :SDeptName, :SPosition, :SPositionName, :SPerson, :SPersonName, :SOrgURL, :Priority, :CreateTime, :StartTime, escription, :State, :FuncURL, :TypeName, :ExecuteMode, :PreemptMode, astChangeTime, :NeedProcess, :RPersonNames)';
  c_Insert_TTaskMessage = 'insert into TTASKMESSAGE(FGUID, FPARENTGUID, FTASKGUID, FROGN, FROGNNAME, FRDEPT, FRDEPTNAME, FRPOSITION, FRPOSITIONNAME, FRPERSON, FRPERSONNAME, FRORGURL, FRECEIVETIME, FSTATE) ' +
    'values(:GUID, :PARENTGUID, :TASKGUID, :ROGN, :ROGNNAME, :RDEPT, :RDEPTNAME, :RPOSITION, :RPOSITIONNAME, :RPERSON, :RPERSONNAME, :RORGURL, :RECEIVETIME, :STATE)';
  c_Insert_TTaskBizData = 'insert into TTASKBIZDATA(FGUID, FTASKGUID, FKEY0, FKEYVALUE0) ' +
    'values(:GUID, :TaskGUID, :Key0, :KeyValue0)';
var
  lQuery: TQuery;
  lTransactionHandle: TTransactionHandle;
  lProcURL, lFlowFieldURL, lFlowID: string;
  lFlowGUID, lTaskGUID, lTaskMessageGUID, lTaskBizDataGUID: string;
  lSenderURL, lReceiverURL: string;
  lSenderOrgan, lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
    lSenderPositionName, lSender, lSenderName: string;
  lReceiverOrgan, lReceiverOrganName, lReceiverDept, lReceiverDeptName,
    lReceiverPosition, lReceiverPositionName, lReceiver, lReceiverName: string;
  lReceiverPersonNames: string;
  I: integer;
begin
  lQuery := TQuery.Create(nil);
  try
    lQuery.ConnectionString := BizObjConsts.cSysDatabaseConnectionString;

    { 一、根据当前任务获取所需任务信息、变量赋值 }
    { 1、任务表 }
    lQuery.CommandText := c_Select_TTask;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的任务。');
    lFlowGUID := lQuery.FieldByName('FFlowGUID').AsString;
    lQuery.Close;
    { 2、任务消息表 }
    lQuery.CommandText := c_Select_TTaskMessage;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的任务消息。');
    lSenderURL := 'Org:' + lQuery.FieldByName('FRORGURL').AsString;
    lQuery.Close;
    { 3、流程表 }
    lQuery.CommandText := c_Select_TFlow;
    lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的流程。');
    lProcURL := lQuery.FieldByName('FPROCURL').AsString;
    lFlowFieldURL := lQuery.FieldByName('FFLOWFIELDURL').AsString;
    lQuery.Close;
    { 4、流程ID表 }
    lQuery.CommandText := c_Select_TFlowID;
    lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
    lQuery.Open;
    Assert(lQuery.RecordCount > 0, '找不到指定GUID的流程ID记录。');
    lFlowID := lQuery.FieldByName('FID').AsString;
    lQuery.Close;



    lTransactionHandle := lQuery.Connection.Transaction.Start(True); //启动事务
    try
      { 二、更新当前流程任务 }

      { 1、Update TTask }
      lQuery.CommandText := c_Update_TTask;
      lQuery.Params.ParamByName('TaskState').AsString := Task.TaskStateToStr(TTaskState.tsReturned); //已回退
      lQuery.Params.ParamByName('FinishTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Execute;
      { 2、Update TTaskMessage }
      lQuery.CommandText := c_Update_TTaskMessage;
      lQuery.Params.ParamByName('TaskMeesageState').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsReturned); //已回退
      lQuery.Params.ParamByName('FinishTime').AsDateTime := Business.Data.SysSrv.SysService.Time;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Execute;



      { 三、生成下一步流程任务数据到数据库相关表 }

      { 1、Add To TTask }
      TOrgUtils.GetOrganDeptPositionPersonIDName(lSenderURL, lSenderOrgan,
        lSenderOrganName, lSenderDept, lSenderDeptName, lSenderPosition,
        lSenderPositionName, lSender, lSenderName);
      lReceiverPersonNames := TOrgUtils.GetOrgURLsDisplayName(AReceiverURLs, -1);
      lTaskGUID := jsCommon.CreateGUIDStr;

      lQuery.CommandText := c_Insert_TTask;
      lQuery.Params.ParamByName('GUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('ParentGUID').AsString := '-1';
      lQuery.Params.ParamByName('PREVGUID').AsString := ACurrentTaskGUID; //新建任务的上一任务关联到当前任务
      lQuery.Params.ParamByName('NextGUID').AsString := '-1';
      lQuery.Params.ParamByName('GroupGUID').AsString := '-1';
      lQuery.Params.ParamByName('ProcURL').AsString := SysUtils.UpperCase(lProcURL);
      lQuery.Params.ParamByName('ProcUnitID').AsString := APrevProcUnitID;
      lQuery.Params.ParamByName('Subject').AsString := ATaskSubject;
      lQuery.Params.ParamByName('Kind').AsString := Flow.FlowTaskKindToStr(TFlowTaskKind.ftkReturn); //'ftkReturn';
      lQuery.Params.ParamByName('SOGN').AsString := lSenderOrgan;
      lQuery.Params.ParamByName('SOGNNAME').AsString := lSenderOrganName;
      lQuery.Params.ParamByName('SDept').AsString := lSenderDept;
      lQuery.Params.ParamByName('SDeptName').AsString := lSenderDeptName;
      lQuery.Params.ParamByName('SPosition').AsString := lSenderPosition;
      lQuery.Params.ParamByName('SPositionName').AsString := lSenderPositionName;
      lQuery.Params.ParamByName('SPerson').AsString := FileSys.FileUtils.RemoveFilePrefix(lSender);
      lQuery.Params.ParamByName('SPersonName').AsString := lSenderName;
      lQuery.Params.ParamByName('SOrgURL').AsString := FileSys.FileUtils.RemoveFilePrefix(lSenderURL);
      lQuery.Params.ParamByName('Priority').AsString := 'tpNormal';
      lQuery.Params.ParamByName('CreateTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('StartTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('Description').AsString := '';
      lQuery.Params.ParamByName('State').AsString := Task.TaskStateToStr(TTaskState.tsStarted);
      lQuery.Params.ParamByName('FuncURL').AsString := ATaskFuncURL;
      lQuery.Params.ParamByName('TypeName').AsString := APrevProcUnitName;
      lQuery.Params.ParamByName('ExecuteMode').AsString := TFlowTaskUtils.TaskExecuteModeToStr(AExecuteMode);
      lQuery.Params.ParamByName('PreemptMode').AsString :=  TFlowTaskUtils.TaskPreemptModeToStr(APreemptMode);
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('NeedProcess').AsString := 'Y';
      lQuery.Params.ParamByName('RPersonNames').AsString := lReceiverPersonNames;
      lQuery.Execute;

      { 2、Add TTaskMessage }
      for I := 0 to AReceiverURLs.Count - 1 do
      begin
        lReceiverURL := AReceiverURLs[I];
        TOrgUtils.GetOrganDeptPositionPersonIDName(lReceiverURL, lReceiverOrgan,
          lReceiverOrganName, lReceiverDept, lReceiverDeptName, lReceiverPosition,
          lReceiverPositionName, lReceiver, lReceiverName);

        lTaskMessageGUID := jsCommon.CreateGUIDStr;

        lQuery.CommandText := c_Insert_TTaskMessage;
        lQuery.Params.ParamByName('GUID').AsString := lTaskMessageGUID;
        lQuery.Params.ParamByName('PARENTGUID').AsString := '-1';
        lQuery.Params.ParamByName('TASKGUID').AsString := lTaskGUID;
        lQuery.Params.ParamByName('ROGN').AsString := lReceiverOrgan;
        lQuery.Params.ParamByName('ROGNNAME').AsString := lReceiverOrganName;
        lQuery.Params.ParamByName('RDEPT').AsString := lReceiverDept;
        lQuery.Params.ParamByName('RDEPTNAME').AsString := lReceiverDeptName;
        lQuery.Params.ParamByName('RPOSITION').AsString := lReceiverPosition;
        lQuery.Params.ParamByName('RPOSITIONNAME').AsString := lReceiverPositionName;
        lQuery.Params.ParamByName('RPERSON').AsString := lReceiver;
        lQuery.Params.ParamByName('RPERSONNAME').AsString := lReceiverName;
        lQuery.Params.ParamByName('RORGURL').AsString := FileSys.FileUtils.RemoveFilePrefix(lReceiverURL);
        lQuery.Params.ParamByName('RECEIVETIME').AsDateTime := SysSrv.SysService.Time;
        lQuery.Params.ParamByName('STATE').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsReceived);
        lQuery.Execute;
      end;

      { 3、Add TTaskBizData }
      lTaskBizDataGUID := jsCommon.CreateGUIDStr;
      lQuery.CommandText := c_Insert_TTaskBizData;
      lQuery.Params.ParamByName('GUID').AsString := lTaskBizDataGUID;
      lQuery.Params.ParamByName('TaskGUID').AsString := lTaskGUID;
      lQuery.Params.ParamByName('Key0').AsString := lFlowFieldURL;
      lQuery.Params.ParamByName('KeyValue0').AsString := lFlowID;
      lQuery.Execute;

      lQuery.Connection.Transaction.Commit(lTransactionHandle);  //提交事务
    except
      lQuery.Connection.Transaction.Rollback(lTransactionHandle); //回滚事务
      raise;
    end;
  finally
    lQuery.Free;
  end;
end;

static procedure TFlowControlService.FlowBack(ACurrentTaskGUID, APrevProcUnitID, APrevProcUnitName,
  ATaskSubject, ATaskFuncURL, AReceiverURL: string);
var
  lReceiverURLs: TStrings;
begin
  lReceiverURLs := TStringList.Create;
  try
    lReceiverURLs.Add(AReceiverURL);
    TFlowControlService.FlowBack(ACurrentTaskGUID, APrevProcUnitID, APrevProcUnitName,
      ATaskSubject, ATaskFuncURL, lReceiverURLs, TTaskExecuteMode.emExclusive,
      TTaskPreemptMode.omFirstProcess);
  finally
    lReceiverURLs.Free;
  end;
end;

static procedure TFlowControlService.FlowAbort(ACurrentTaskGUID: string);
const
  c_Select_FlowGUID = 'select FFLOWGUID from TTASK where FGUID = :TaskGUID';

  c_Update_TTask = 'update TTASK set FNEXTGUID = ''-1'', FSTATE = :TaskState, FLASTCHANGETIME = astChangeTime where FGUID = :TaskGUID';
  c_Update_TTaskMessage = 'update TTASKMESSAGE set FSTATE = :TaskMeesageState where FTASKGUID = :TaskGUID';
  c_Update_TFlow = 'update TFLOW set FSTATE = :FlowState where FGUID = :FlowGUID';
var
  lQuery: TQuery;
  lFlowGUID: string;
  lTransactionHandle: TTransactionHandle;
begin
  lQuery := TQuery.Create(nil);
  try
    lQuery.ConnectionString := BizObjConsts.cSysDatabaseConnectionString;

    { 一、根据当前任务获取所需流程信息、变量赋值 }
    lQuery.CommandText := c_Select_FlowGUID;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    lFlowGUID := lQuery.FieldByName('FFLOWGUID').AsString;
    Assert(lFlowGUID <> '', 'FlowGUID不允许为空');

    { 二、更新当前任务流程状态、时间信息 }
    lTransactionHandle := lQuery.Connection.Transaction.Start(True); //启动事务
    try
      { 1、更新 TFlow 表 }
      lQuery.CommandText := c_Update_TFlow;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('FlowState').AsString := Flow.FlowStateToStr(TFlowState.fsAborted);
      lQuery.Execute;

      { 2、更新 TTask 表 }
      lQuery.CommandText := c_Update_TTask;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Params.ParamByName('TaskState').AsString := Task.TaskStateToStr(TTaskState.tsAborted);
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Execute;

      { 3、更新 TTaskMessage 表 }
      lQuery.CommandText := c_Update_TTaskMessage;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Params.ParamByName('TaskMeesageState').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsAborted);
      lQuery.Execute;

      lQuery.Connection.Transaction.Commit(lTransactionHandle);  //提交事务
    except
      lQuery.Connection.Transaction.Rollback(lTransactionHandle); //回滚事务
      raise;
    end;
  finally
    lQuery.Free;
  end;
end;

static procedure TFlowControlService.FlowFinish(ACurrentTaskGUID: string);
const
  c_Select_FlowGUID = 'select FFLOWGUID from TTASK where FGUID = :TaskGUID';

  c_Update_TFlow = 'update TFLOW set FSTATE = :FlowState FFINISHTIME = :FinishTime where FGUID = :FlowGUID';
  c_Update_TTask = 'update TTASK set FSTATE = :TaskState, FFINISHTIME = :FinishTime, FLASTCHANGETIME = astChangeTime where FGUID = :TaskGUID';
  c_Update_TTaskMessage = 'update TTASKMESSAGE set FSTATE = :TaskMeesageState, FFINISHTIME = :FinishTime where FTASKGUID = :TaskGUID';
var
  lQuery: TQuery;
  lFlowGUID: string;
  lTransactionHandle: TTransactionHandle;
begin
  lQuery := TQuery.Create(nil);
  try
    lQuery.ConnectionString := BizObjConsts.cSysDatabaseConnectionString;

    { 一、根据当前任务获取所需流程信息、变量赋值 }
    lQuery.CommandText := c_Select_FlowGUID;
    lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
    lQuery.Open;
    lFlowGUID := lQuery.FieldByName('FFLOWGUID').AsString;
    Assert(lFlowGUID <> '', 'FlowGUID不允许为空');

    { 二、更新当前任务流程状态、时间信息 }
    lTransactionHandle := lQuery.Connection.Transaction.Start(True); //启动事务
    try
      { 1、更新 TFlow 表 }
      lQuery.CommandText := c_Update_TFlow;
      lQuery.Params.ParamByName('FlowGUID').AsString := lFlowGUID;
      lQuery.Params.ParamByName('FlowState').AsString := Flow.FlowStateToStr(TFlowState.fsFinished);
      lQuery.Params.ParamByName('FinishTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Execute;

      { 2、更新 TTask 表 }
      lQuery.CommandText := c_Update_TTask;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Params.ParamByName('TaskState').AsString := Task.TaskStateToStr(TTaskState.tsFinished);
      lQuery.Params.ParamByName('LastChangeTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Params.ParamByName('FinishTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Execute;

      { 3、更新 TTaskMessage 表 }
      lQuery.CommandText := c_Update_TTaskMessage;
      lQuery.Params.ParamByName('TaskGUID').AsString := ACurrentTaskGUID;
      lQuery.Params.ParamByName('TaskMeesageState').AsString := Task.TaskMessageStateToStr(TTaskMessageState.tmsFinished);
      lQuery.Params.ParamByName('FinishTime').AsDateTime := SysSrv.SysService.Time;
      lQuery.Execute;

      lQuery.Connection.Transaction.Commit(lTransactionHandle);  //提交事务
    except
      lQuery.Connection.Transaction.Rollback(lTransactionHandle); //回滚事务
      raise;
    end;
  finally
    lQuery.Free;
  end;
end;
回复 支持 反对

使用道具 举报

发表于 2009-7-8 08:26:09 | 显示全部楼层
收藏备用。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Justep Inc.

GMT+8, 2025-7-19 02:22 , Processed in 0.043002 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表