// 同步附件
  private void syscAttach(Case4InfractBusiness cib, String procInstId, String taskId) {
    if (cib == null || procInstId == null || procInstId.length() == 0) return;

    List<Attach> attachs =
        attachService.findByPtype(Case4InfractBusiness.ATTACH_TYPE, cib.getUid());
    if (attachs == null || attachs.size() == 0) return;

    for (Attach attach : attachs) {
      // 复制附件到流程附件位置中----开始---
      // 扩展名
      String extension = StringUtils.getFilenameExtension(attach.getPath());
      // 文件存储的相对路径(年月),避免超出目录内文件数的限制
      String subFolder = DateUtils.formatCalendar(Calendar.getInstance(), "yyyyMM");
      // 上传文件存储的绝对路径
      String appRealDir = Attach.DATA_REAL_PATH + File.separator + FlowAttach.DATA_SUB_PATH;
      // 所保存文件所在的目录的绝对路径名
      String realFileDir = appRealDir + File.separator + subFolder;
      // 不含路径的文件名
      String fileName =
          DateUtils.formatCalendar(Calendar.getInstance(), "yyyyMMddHHmmssSSSS") + "." + extension;
      // 所保存文件的绝对路径名
      String realFilePath = realFileDir + File.separator + fileName;
      // 构建文件要保存到的目录
      File _fileDir = new File(realFileDir);
      if (!_fileDir.exists()) {
        if (logger.isFatalEnabled()) logger.fatal("mkdir=" + realFileDir);
        _fileDir.mkdirs();
      }
      // 直接复制附件
      if (logger.isInfoEnabled()) logger.info("pure copy file");

      // 附件路径
      String path = Attach.DATA_REAL_PATH + File.separator + attach.getPath();

      // 从附件目录下的指定文件复制到attachment目录下
      try {
        FileCopyUtils.copy(new FileInputStream(new File(path)), new FileOutputStream(realFilePath));
      } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
      }

      // 复制附件到流程附件位置中----结束---

      // 插入流程附件记录信息
      FlowAttach flowAttach = new FlowAttach();
      flowAttach.setUid(idGeneratorService.next(FlowAttach.ATTACH_TYPE));
      flowAttach.setType(FlowAttach.TYPE_ATTACHMENT); // 类型:1-附件,2-意见
      flowAttach.setPid(procInstId); // 流程id
      flowAttach.setPath(subFolder + File.separator + fileName); // 附件路径,物理文件保存的相对路径
      flowAttach.setExt(extension); // 扩展名
      flowAttach.setSubject(attach.getSubject()); // 标题
      flowAttach.setSize(attach.getSize());
      flowAttach.setFormatted(false); // 附件是否需要格式化

      if (taskId == null) {
        flowAttach.setCommon(true); // 公共附件
      } else {
        flowAttach.setCommon(false); // 任务附件
        flowAttach.setTid(taskId);
      }

      // 创建人,最后修改人信息
      SystemContext context = SystemContextHolder.get();
      flowAttach.setAuthor(context.getUserHistory());
      flowAttach.setModifier(context.getUserHistory());
      flowAttach.setFileDate(Calendar.getInstance());
      flowAttach.setModifiedDate(Calendar.getInstance());
      this.flowAttachService.save(flowAttach);
    }
  }
  // 保存一个附件记录
  private Attach saveAttachLog(
      HttpServletRequest request,
      String localFile,
      String ptype,
      String puid,
      SystemContext context,
      String extend,
      long size,
      Calendar now,
      String path,
      boolean absolute) {
    // 剔除文件名中的路径部分
    int li = localFile.lastIndexOf("/");
    if (li == -1) li = localFile.lastIndexOf("\\");
    if (li != -1) localFile = localFile.substring(li + 1);

    // 创建附件记录
    Attach attach = new Attach();
    attach.setAuthor(context.getUserHistory());
    attach.setPtype(ptype);
    attach.setPuid(puid);
    attach.setFormat(extend);
    attach.setFileDate(now);
    attach.setPath(path);
    attach.setSize(size);
    attach.setSubject(localFile);
    attach.setAppPath(!absolute);
    attach = this.getAttachService().save(attach);

    // 创建附件上传日志
    AttachHistory history = new AttachHistory();
    history.setPtype(Attach.class.getSimpleName());
    history.setPuid(attach.getId().toString());
    history.setType(AttachHistory.TYPE_UPLOAD);
    history.setAuthor(context.getUserHistory());
    history.setFileDate(now);
    history.setPath(path);
    history.setAppPath(false);
    history.setFormat(attach.getFormat());
    history.setSubject(attach.getSubject());
    String[] c = WebUtils.getClient(request);
    history.setClientIp(c[0]);
    history.setClientInfo(c[2]);
    this.getAttachService().saveHistory(history);

    return attach;
  }
  private void sendEmail(SubscribeEvent event, List<Actor> actors, OperateLog worklog) {
    // 工作日志的详细内容
    String worklog_content = worklog.getContent();
    Email email = new Email();
    worklog_content += "发送方式:邮件[";

    String emailUid = this.idGeneratorService.nexttvak(Email.ATTACH_TYPE);
    email.setUid(emailUid);
    email.setStatus(Email.STATUS_SENDED);
    email.setType(Email.TYPE_NEW);
    email.setFileDate(Calendar.getInstance());
    email.setSendDate(Calendar.getInstance());
    email.setSubject(event.getSubject());
    worklog_content += "邮件主题: " + event.getSubject();

    Map<String, Object> args = new HashMap<String, Object>();
    args.put("content", event.getContent());

    // 根据事件的CODE 加上 “-EMAIL”后缀 查找模板中是否有配置 自定义的模板
    Template custom = this.templateService.loadByCode(event.getCode() + "-EMAIL");

    if (custom != null) {
      email.setContent(this.templateService.format(custom.getCode(), args));
    } else { // 使用默认的邮件模板
      email.setContent(this.templateService.format("BC-EMAIL-SYSTEMAUTOFORWARD", args));
    }
    worklog_content += ",邮件内容: " + event.getContent();
    // 系统管理员发送
    Actor admin = this.actorService.loadByCode("admin");
    email.setSender(admin);

    if (event.getAttachs() != null) {
      for (Attach attach : event.getAttachs()) {
        // 复制附件
        this.attachService.doCopy(
            attach.getPtype(), attach.getPuid(), Email.ATTACH_TYPE, emailUid, true);
      }
    }

    // 设置发送人
    Set<EmailTo> emailTos = new HashSet<EmailTo>();
    worklog_content += ",邮件接收人:";
    EmailTo et;
    int i = 0;
    for (Actor a : actors) {
      et = new EmailTo();
      et.setEmail(email);
      et.setRead(false);
      et.setReceiver(a);
      // 正常发送
      et.setType(EmailTo.TYPE_TO);
      et.setOrderNo(i);
      emailTos.add(et);

      if (i > 0) worklog_content += "、";
      worklog_content += a.getName();
      i++;
    }
    worklog_content += "]";
    worklog.setContent(worklog_content);
    email.setTos(emailTos);

    this.emailService.save(email);
  }
  public Attach getAttach(
      String subject,
      String code,
      Map<String, Object> args,
      String ptype,
      String puid,
      ActorHistory author,
      Map<String, Object> formatParamSql)
      throws Exception {
    Assert.hasText(subject, "subject is Empty");
    Assert.hasText(code, "code is Empty");
    Assert.hasText(ptype, "ptype is Empty");
    Assert.hasText(puid, "puid is Empty");

    Template template = this.templateDao.loadByCode(code);
    if (template == null) throw new CoreException("Template code:" + code + " not find entity!");

    if (args == null) args = new HashMap<String, Object>();

    if (author == null) {
      if (SystemContextHolder.get() == null) {
        author = this.actorHistoryService.loadByCode("admin");
      } else {
        author = SystemContextHolder.get().getUserHistory();
      }
    }

    Map<String, Object> args4Param = this.getMapParams(template.getId(), formatParamSql);

    if (args4Param != null)
      // 最终替换参数
      args.putAll(this.getMapParams(template.getId(), formatParamSql));

    // 生成附件
    Attach attach = new Attach();
    attach.setAuthor(author);
    attach.setFileDate(Calendar.getInstance());
    attach.setSubject(subject);
    attach.setAppPath(false);
    attach.setFormat(template.getTemplateType().getExtension());
    attach.setStatus(BCConstants.STATUS_ENABLED);
    attach.setPtype(ptype);
    attach.setPuid(puid);

    // 文件存储的相对路径(年月),避免超出目录内文件数的限制
    Calendar now = Calendar.getInstance();
    String datedir = new SimpleDateFormat("yyyyMM").format(now.getTime());

    // 要保存的物理文件
    String realpath; // 绝对路径名
    // uuid
    String fileName =
        UUID.randomUUID().toString().replace("-", "")
            + "."
            + template.getTemplateType().getExtension(); // 不含路径的文件名
    realpath = Attach.DATA_REAL_PATH + "/" + datedir + "/" + fileName;

    // 构建文件要保存到的目录
    File file = new File(realpath);
    if (!file.getParentFile().exists()) {
      if (logger.isInfoEnabled()) {
        logger.info("mkdir=" + file.getParentFile().getAbsolutePath());
      }
      file.getParentFile().mkdirs();
    }

    OutputStream out = new BufferedOutputStream(new FileOutputStream(file));

    this.formatTo(template.getCode(), args, out);

    // 设置附件大小
    attach.setSize(new File(realpath).length());

    // 设置附件相对路径
    attach.setPath(datedir + "/" + fileName);

    return this.attachService.save(attach);
  }