起步软件技术论坛-X3

 找回密码
 立即注册
搜索
查看: 835|回复: 5

【X3上的大型企业应用实战案例分享】一、X3平台外的流程控制纯SQL语句实现

[复制链接]
发表于 2009-7-3 13:56:00 | 显示全部楼层 |阅读模式
【X3上的大型企业综合应用系统开发实战案例分享系列】一、X3平台外的流程控制纯SQL语句实现:流程启动、向下流转、回退、中止,流程结束。

以下是完整源代码:



{ 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-3 13:59:42 | 显示全部楼层
顶!!!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-7-3 14:12:07 | 显示全部楼层

此实现用于何处?有何价值?

在此基础之上,加上相应的流程自定义规则配置,可以实现符合自己企业的手机WAP流程控制处理。弥补X3平台不支持手机审批、流程处理的不足。
回复 支持 反对

使用道具 举报

发表于 2011-6-23 08:10:24 | 显示全部楼层
TFlowTaskUtils.TaskPreemptModeToStr 要如何引用?
回复 支持 反对

使用道具 举报

发表于 2011-6-28 11:26:19 | 显示全部楼层

TFlowTaskUtils.TaskPreemptModeToStr 也是自己写的,共享如下:

static function TFlowTaskUtils.TaskPreemptModeToStr(TaskPreemptMode: Business.Model.Flow.TTaskPreemptMode): string;
begin
  case TaskPreemptMode of
    Business.Model.Flow.TTaskPreemptMode.omFirstProcess:
      Result := 'omFirstProcess';
    Business.Model.Flow.TTaskPreemptMode.omFirstOpen:
      Result := 'omFirstOpen';
  else
    Result := '';
  end;
end;
回复 支持 反对

使用道具 举报

发表于 2011-7-4 11:25:37 | 显示全部楼层
谢谢分享
:thumbsup: :thumbsup:
回复 支持 反对

使用道具 举报

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

本版积分规则

小黑屋|手机版|Justep Inc.

GMT+8, 2024-4-29 06:42 , Processed in 0.047707 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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