Browse Source

oss文件上传部分功能

user5 10 months atrás
parent
commit
991546f80d
20 changed files with 3359 additions and 10 deletions
  1. 6 0
      jeeplus-modules/jeeplus-test/pom.xml
  2. 173 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/controller/OssController.java
  3. 98 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/controller/OssFileController.java
  4. 69 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/domain/WorkAttachment.java
  5. 30 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/mapper/OssServiceMapper.java
  6. 96 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/mapper/xml/OssServiceMapper.xml
  7. 333 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/OSSClientService.java
  8. 281 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/OssService.java
  9. 11 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/dto/FileDetailDTO.java
  10. 6 0
      jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/dto/OssServiceDto.java
  11. 211 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/security/util/DaoAuthenticationProvider.java
  12. 151 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Encodes.java
  13. 71 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Exceptions.java
  14. 289 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/FileKit.java
  15. 520 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Global.java
  16. 86 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/SpringContextHolder.java
  17. 849 0
      jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/StringUtils.java
  18. 37 1
      jeeplus-platform/jeeplus-common/src/main/java/com/jeeplus/config/properties/JeePlusProperties.java
  19. 24 5
      jeeplus-web/src/main/resources/application-development.yml
  20. 18 4
      jeeplus-web/src/main/resources/application-production.yml

+ 6 - 0
jeeplus-modules/jeeplus-test/pom.xml

@@ -39,6 +39,12 @@
             <artifactId>fastjson</artifactId>
             <version>1.2.73</version>
         </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.6.0</version>
+            <scope>compile</scope>
+        </dependency>
 
 
     </dependencies>

+ 173 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/controller/OssController.java

@@ -0,0 +1,173 @@
+package com.jeeplus.test.oss.controller;
+
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.auth.sts.AssumeRoleRequest;
+import com.aliyuncs.auth.sts.AssumeRoleResponse;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.http.ProtocolType;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.jeeplus.common.utils.FileUtils;
+import com.jeeplus.sys.utils.FileKit;
+import com.jeeplus.test.oss.service.OSSClientService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Api(tags ="oss")
+@RestController
+@RequestMapping(value = "/oss/oss")
+public class OssController {
+
+    private static final String DIRECTORY = "/attachment-file";
+
+    private static final String ALIYUNURL = "http://oss.gangwaninfo.com";
+
+    private static final String ALIYUNDOWNLOADURL = "";
+
+    @Value("${config.accessory.aliyun.bucketName}")
+    private String bucketName;
+
+    @Value("${config.accessory.aliyun.endpoint}")
+    private String endpoint;
+
+    @Autowired
+    private OSSClientService ossClientService;
+
+    /**
+     * 文件上传
+     * @param storeAs
+     * @param file
+     * @return
+     * @throws Exception
+     */
+    @ApiOperation(value = "文件上传")
+    @PostMapping("/upload")
+    public ResponseEntity<String> upload(@RequestParam String storeAs, @RequestParam("file") MultipartFile file) throws Exception {
+        //取得上传文件
+        if (file != null && !file.isEmpty()) {
+            // 文件保存路径
+            String realPath =DIRECTORY.replace("/","")+"/"+storeAs+ossClientService.datePath()+"/"+ System.currentTimeMillis();
+            //文件原名称
+            String newName = file.getOriginalFilename();
+            ossClientService.uploadFile2OSS(file.getInputStream(),realPath,newName);
+            String filepath = ALIYUNURL + "/" + realPath + newName;
+            return ResponseEntity.ok (filepath);
+        }
+        return ResponseEntity.ok(null);
+    }
+
+    @PostMapping("/getAccess")
+    @ApiOperation(value = "文件上传(前端调用)")
+    public Map<Object, Object> getAccess() {
+        // 只有 RAM用户(子账号)才能调用 AssumeRole 接口
+        // 阿里云主账号的AccessKeys不能用于发起AssumeRole请求
+        // 请首先在RAM控制台创建一个RAM用户,并为这个用户创建AccessKeys
+        String accessKeyId =  "LTAI5tBBnQdpZapU28Ds89fb";
+        String accessKeySecret = "A37CuoALjoxCjPolzRm3ct5o8UHILF";
+        // RoleArn 需要在 RAM 控制台上获取
+        String roleArn = "acs:ram::1132186699724035:role/yf";
+        long durationSeconds = Long.parseLong("3600");
+        String policy = "{\n" +
+                "  \"Version\": \"1\",\n" +
+                "  \"Statement\": [\n" +
+                "    {\n" +
+                "      \"Effect\": \"Allow\",\n" +
+                "      \"Action\": [\n" +
+                "        \"oss:*\"\n" +
+                "      ],\n" +
+                "      \"Resource\": [\n" +
+                "        \"acs:oss:*:*:*\"\n" +
+                "      ],\n" +
+                "      \"Condition\": {}\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}";
+        // RoleSessionName 是临时Token的会话名称,自己指定用于标识你的用户,主要用于审计,或者用于区分Token颁发给谁
+        // 但是注意RoleSessionName的长度和规则,不要有空格,只能有'-' '_' 字母和数字等字符
+        // 具体规则请参考API文档中的格式要求
+        String roleSessionName = "yf";
+        // 此处必须为 HTTPS
+        ProtocolType protocolType = ProtocolType.HTTPS;
+        try {
+            final AssumeRoleResponse stsResponse = assumeRole(accessKeyId, accessKeySecret, roleArn, roleSessionName,
+                    policy, protocolType, durationSeconds);
+
+            Map<Object, Object> respMap = new HashMap<Object, Object>();
+            respMap.put("status", "200");
+            respMap.put("AccessKeyId", stsResponse.getCredentials().getAccessKeyId());
+            respMap.put("AccessKeySecret", stsResponse.getCredentials().getAccessKeySecret());
+            respMap.put("SecurityToken", stsResponse.getCredentials().getSecurityToken());
+            respMap.put("Expiration", stsResponse.getCredentials().getExpiration());
+            respMap.put("AliyunUrl",ALIYUNDOWNLOADURL);
+            respMap.put("Bucket", bucketName);
+            respMap.put("Endpoint", endpoint);
+            return respMap;
+        } catch (ClientException e) {
+            log.error("调用ali获取临时token报错,错误信息:" + e.getMessage());
+            Map<Object, Object> respMap = new HashMap<Object, Object>();
+            respMap.put("status", e.getErrCode());
+            respMap.put("AccessKeyId", "");
+            respMap.put("AccessKeySecret", "");
+            respMap.put("SecurityToken", "");
+            respMap.put("Expiration", "");
+            respMap.put("AliyunUrl", "");
+            respMap.put("Bucket", "");
+            respMap.put("Endpoint", "");
+            return respMap;
+        }
+    }
+
+    public static final String REGION_CN_HANGZHOU = "cn-hangzhou";
+    public static final String STS_API_VERSION = "2015-04-01";
+    protected AssumeRoleResponse assumeRole(String accessKeyId, String accessKeySecret, String roleArn,
+                                            String roleSessionName, String policy, ProtocolType protocolType, long durationSeconds) throws ClientException
+    {
+        try {
+            // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
+            IClientProfile profile = DefaultProfile.getProfile(REGION_CN_HANGZHOU, accessKeyId, accessKeySecret);
+            DefaultAcsClient client = new DefaultAcsClient(profile);
+            // 创建一个 AssumeRoleRequest 并设置请求参数
+            final AssumeRoleRequest request = new AssumeRoleRequest();
+            request.setVersion(STS_API_VERSION);
+            request.setMethod(MethodType.POST);
+            request.setProtocol(protocolType);
+            request.setRoleArn(roleArn);
+            request.setRoleSessionName(roleSessionName);
+            request.setPolicy(policy);
+            request.setDurationSeconds(durationSeconds);
+            // 发起请求,并得到response
+            final AssumeRoleResponse response = client.getAcsResponse(request);
+            return response;
+        } catch (ClientException e) {
+            throw e;
+        }
+    }
+
+    /**
+     * 根据url删除文件
+     * @param url
+     * @return
+     */
+    @ApiOperation("根据url删除文件")
+    @GetMapping("/deleteByUrl")
+    public ResponseEntity delFileByUrl(@RequestParam String url) {
+        String id = FileKit.getFileDir(url);
+        if(FileUtils.delFile(id)){
+            return ResponseEntity.ok ("删除文件成功");
+        }else{
+            return ResponseEntity.badRequest().body ("删除文件失败");
+        }
+
+    }
+}

+ 98 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/controller/OssFileController.java

@@ -0,0 +1,98 @@
+package com.jeeplus.test.oss.controller;
+
+import com.jeeplus.test.oss.domain.WorkAttachment;
+import com.jeeplus.test.oss.service.OssService;
+import com.jeeplus.test.oss.service.dto.FileDetailDTO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@Slf4j
+@Api(tags ="oss")
+@RestController
+@RequestMapping(value = "/oss/file")
+public class OssFileController {
+
+    @Value("${config.accessory.aliyun.aliyunUrl}")
+    private String aliyunUrl;
+
+    @Value("${config.accessory.aliyun.aliyunDownloadUrl}")
+    private String aliyunDownloadUrl;
+
+    @Resource
+    private OssService ossService;
+
+    @ApiOperation(value = "保存数据")
+    @PostMapping("/saveMsg")
+    public void saveMsg(@RequestBody List<WorkAttachment> workAttachments) {
+        ossService.saveMsg(workAttachments);
+    }
+
+    @GetMapping("/deleteMsgByFileName")
+    @ApiOperation(value = "删除数据")
+    public void deleteMsgByFileName(@RequestParam String url) {
+        ossService.deleteMsgByFileName(url);
+    }
+
+    @GetMapping("/deleteMsgById")
+    @ApiOperation(value = "根据id删除数据")
+    public void deleteMsgById(@RequestParam String id) {
+        ossService.deleteMsgById(id);
+    }
+
+    @GetMapping("/getTemporaryUrl")
+    @ApiOperation(value = "根据url获取临时文件地址")
+    public ResponseEntity<String> getTemporaryUrl(@RequestParam String url) {
+        String temporaryLookUrl = ossService.getFileTemporaryLookUrl(aliyunUrl + url);
+        return ResponseEntity.ok(temporaryLookUrl);
+    }
+
+    @GetMapping("/findFileList")
+    @ApiOperation(value = "查询数据")
+    public ResponseEntity<List<WorkAttachment>> findFileList(@RequestParam("attachmentId") String attachmentId) {
+        List<WorkAttachment> list = ossService.findFileList(attachmentId);
+        return ResponseEntity.ok(list);
+    }
+
+    /**
+     * 下载附件
+     */
+    @GetMapping("/downLoadAttach")
+    @ApiOperation(value = "下载附件")
+    public String downLoadAttach(@RequestParam String file, HttpServletResponse response, HttpServletRequest request) throws IOException {
+        file = "http://oss.gangwaninfo.com" + file;
+        file = file.replace("amp;", "");
+        String fileName = file.substring(file.lastIndexOf("/") + 1, file.length());
+        String cons = "";
+        if (file.contains(aliyunUrl)) {
+            cons = aliyunUrl;
+        } else if (file.contains("http://gangwan-app.oss-cn-hangzhou.aliyuncs.com")) {
+            cons = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com";
+        } else {
+            cons = aliyunDownloadUrl;
+        }
+        String key = file.split(cons + "/")[1];
+        log.info("-----------------------------------------");
+        log.info("fileName=" + fileName);
+        log.info("key=" + key);
+        log.info("-----------------------------------------");
+        ossService.downByStream(key, fileName, response, request.getHeader("USER-AGENT"));
+        return null;
+    }
+
+    @GetMapping("/getFileSizeByUrl")
+    @ApiOperation(value = "根据文件路径获取文件信息")
+    public ResponseEntity<FileDetailDTO> getFileSizeByUrl(@RequestParam("url") String url) {
+        FileDetailDTO fileDetailDTO = ossService.getFileSizeByUrl(url);
+        return ResponseEntity.ok(fileDetailDTO);
+    }
+}

+ 69 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/domain/WorkAttachment.java

@@ -0,0 +1,69 @@
+package com.jeeplus.test.oss.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.jeeplus.core.domain.BaseEntity;
+import com.jeeplus.core.service.dto.BaseDTO;
+import lombok.Data;
+
+@Data
+@TableName("work_attachment")
+public class WorkAttachment extends BaseDTO {
+
+    /**
+     * 附件地址
+     */
+    private String url;
+
+    /**
+     * 文件类型(文件后缀名)
+     */
+    private String type;
+
+    /**
+     * 附件对应父节点id(记录是谁的id)
+     */
+    private String attachmentId;
+
+    /**
+     * 文件名
+     */
+    private String attachmentName;
+
+    /**
+     * 文件所属业务模块(数据字典配置)
+     */
+    private String attachmentFlag;
+
+    /**
+     * 所属模块子模块
+     */
+    private String moduleType;
+
+    /**
+     * 附件类型
+     */
+    private String attachmentType;
+
+    /**
+     * 附件大小
+     */
+    private String fileSize;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
+    /**
+     * 文件描述
+     */
+    private String description;
+
+    /**
+     * 附件临时地址
+     *该属性不是实体字段
+     */
+    @TableField(exist = false)
+    private String temporaryUrl;
+}

+ 30 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/mapper/OssServiceMapper.java

@@ -0,0 +1,30 @@
+package com.jeeplus.test.oss.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.jeeplus.sys.service.dto.UserDTO;
+import com.jeeplus.test.oss.domain.WorkAttachment;
+import org.apache.ibatis.annotations.Param;
+import org.flowable.spring.security.UserDto;
+
+import java.util.List;
+
+public interface OssServiceMapper extends BaseMapper<WorkAttachment> {
+
+    Integer selectSaveById(String id);
+
+    void updateProjectRecord(String id);
+
+    List<WorkAttachment> findList(@Param(Constants.WRAPPER) QueryWrapper queryWrapper);
+
+    void insertWorkAttachment (@Param("workAttachment") WorkAttachment workAttachment, @Param("userDto") UserDTO userDto);
+
+    /**
+     * 根据关联id和url查询数据信息
+     * @param workAttachment
+     * @return
+     */
+    List<WorkAttachment> getByAttachmentIdAndUrlAndAttachmentFlag(WorkAttachment workAttachment);
+}

+ 96 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/mapper/xml/OssServiceMapper.xml

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jeeplus.test.oss.mapper.OssServiceMapper">
+
+  <select id="selectSaveById" resultType="java.lang.Integer">
+    SELECT
+        COUNT( 0 )
+    FROM
+        work_attachment
+    WHERE
+        del_flag = 0
+        AND attachment_id = (
+        SELECT
+            attachment_id
+        FROM
+            work_attachment
+    WHERE
+        id = #{id})
+  </select>
+
+  <update id="updateProjectRecord">
+    UPDATE project_records
+    SET file_upload_type = 0
+    WHERE
+        id = (
+        SELECT
+            attachment_id
+        FROM
+            work_attachment
+    WHERE
+        id = #{id})
+  </update>
+
+    <select id="findList" resultType="com.jeeplus.test.oss.domain.WorkAttachment">
+        SELECT a.id, a.url, a.type, a.attachment_id, a.attachment_name, a.attachment_flag,
+        a.module_type, a.attachment_type, a.file_size, a.sort, a.description,
+        a.create_date, a.create_by as "create_by.id", su.name as "createBy.name" , a.update_date, a.update_by as "updateBy.id", a.del_flag
+         FROM work_attachment a
+         left join sys_user su on su.id = a.create_by
+         ${ew.customSqlSegment}
+         ORDER BY a.sort ASC;
+    </select>
+
+    <insert id="insertWorkAttachment">
+        INSERT INTO work_attachment (
+            id,
+            create_by,
+            create_date,
+            update_by,
+            update_date,
+            del_flag,
+            url,
+            type,
+            attachment_id,
+            attachment_name,
+            attachment_flag,
+            file_size,
+            sort
+            )
+        VALUES(
+            #{workAttachment.id},
+            #{userDto.id},
+            #{workAttachment.createDate},
+            #{userDto.id},
+            #{workAttachment.updateDate},
+            #{workAttachment.delFlag},
+            #{workAttachment.url},
+            #{workAttachment.type},
+            #{workAttachment.attachmentId},
+            #{workAttachment.attachmentName},
+            #{workAttachment.attachmentFlag},
+            #{workAttachment.fileSize},
+            #{workAttachment.sort}
+            )
+    </insert>
+
+    <select id="getByAttachmentIdAndUrlAndAttachmentFlag" resultType="com.jeeplus.test.oss.domain.WorkAttachment">
+        SELECT a.id, a.url, a.type, a.attachment_id, a.attachment_name, a.attachment_flag,
+        a.module_type, a.attachment_type, a.file_size, a.sort, a.description,
+        a.create_date, a.create_by as "create_by.id", su.name as "createBy.name" , a.update_date, a.update_by as "updateBy.id", a.del_flag
+         FROM work_attachment a
+         left join sys_user su on su.id = a.create_by
+         <where>
+             a.del_flag = 0
+             <if test="attachmentId != null and attachmentId != ''">
+                 and a.attachment_id = #{attachmentId}
+             </if>
+             <if test="attachmentName != null and attachmentName != ''">
+                 and a.attachment_name = #{attachmentName}
+             </if>
+             <if test="attachmentFlag != null and attachmentFlag != ''">
+                 and a.attachment_flag = #{attachmentFlag}
+             </if>
+         </where>
+    </select>
+</mapper>

+ 333 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/OSSClientService.java

@@ -0,0 +1,333 @@
+package com.jeeplus.test.oss.service;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClient;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.OSSObject;
+import com.aliyun.oss.model.PutObjectResult;
+import com.aliyun.oss.model.SimplifiedObjectMeta;
+import com.jeeplus.sys.utils.Global;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Calendar;
+import java.util.Date;
+
+@Slf4j
+@Service
+public class OSSClientService {
+
+    @Value("${config.accessory.aliyun.aliyunDownloadUrl}")
+    private String aliyunDownloadUrl;
+
+    @Value("${config.accessory.aliyun.aliyunUrl}")
+    private String aliyunUrl;
+
+    @Value("${config.accessory.aliyun.bucketName}")
+    private String bucketName;
+
+    @Value("${qzBucketName}")
+    private String qzBucketName;
+
+    @Value("${config.accessory.aliyun.endpoint}")
+    private String endpoint;
+
+    @Value("${config.accessory.aliyun.accessKeyId}")
+    private String accessKeyId;
+
+    @Value("${config.accessory.aliyun.accessKeySecret}")
+    private String accessKeySecret;
+
+    /**
+     * 上传到OSS服务器  如果同名文件会覆盖服务器上的
+     *
+     * @param fileName 文件名称 包括后缀名
+     * @return 出错返回"" ,唯一MD5数字签名
+     */
+    public String uploadFile2OSS(InputStream inStream, String fileDir, String fileName) {
+        //初始化OSSClient
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+        long start = System.currentTimeMillis();
+        //上传文件
+        PutObjectResult putResult = ossClient.putObject(bucketName, fileDir + fileName, inStream);
+        String ret = putResult.getETag();
+
+        try {
+            if (inStream != null) {
+                inStream.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        long end = System.currentTimeMillis();
+        log.info("上传文件到云服务器成功,文件名:{},耗时:{}ms",fileName,end-start);
+        return ret;
+    }
+
+    /**
+     * 获得url链接
+     *
+     * @param key
+     * @return
+     */
+    public String getUrl(String key) {
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+        // 设置URL过期时间为10年  3600l* 1000*24*365*10
+        Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10);
+        // 生成URL
+        URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
+        if (url != null) {
+            return url.toString();
+        }
+        return null;
+    }
+
+
+
+    /**
+     * 阿里云获取临时文件大小
+     * @param file
+     */
+    public Long getSimplifiedObjectMeta(String file){
+        //初始化OSSClient
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+
+        URL url = null;
+        SimplifiedObjectMeta simplifiedObjectMeta = new SimplifiedObjectMeta();
+        try {
+
+            file = file.replace("amp;","");
+            String aliyunDownload = aliyunDownloadUrl;
+            String aliDownloadUrl = aliyunUrl;
+            String cons = "";
+            if (file.contains(aliyunDownload)){
+                cons = aliyunDownload;
+            }else if (file.contains("http://gangwan-app.oss-cn-hangzhou.aliyuncs.com")){
+                cons = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com";
+            }else {
+                cons = aliDownloadUrl;
+            }
+            String key = file.split(cons+"/")[1];
+            simplifiedObjectMeta = ossClient.getSimplifiedObjectMeta(bucketName, key);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+        return simplifiedObjectMeta.getSize();
+    }
+
+
+    /**
+     * 文件下载
+     * @param key
+     * @param fileName
+     */
+    public byte[] downBytesByStream(String key, String fileName){
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+        byte[] bytes = null;
+        BufferedInputStream in = null;
+        ByteArrayOutputStream outputStream = null;
+        log.info("开始从云服务器加载文件,文件名:{}",fileName);
+        try {
+            // 创建OSSClient实例
+            long start = System.currentTimeMillis();
+            OSSObject ossObject = ossClient.getObject(bucketName, key);
+            in = new BufferedInputStream(ossObject.getObjectContent());
+            outputStream = new ByteArrayOutputStream();
+            byte[] car=new byte[1024];
+            int L=0;
+            while((L=in.read(car))!=-1){
+                outputStream.write(car, 0,L);
+            }
+            bytes = outputStream.toByteArray();
+            long end = System.currentTimeMillis();
+            log.info("从云服务器加载文件成功,文件名:{},耗时:{}ms",fileName,end-start);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }finally {
+            if (in!=null){
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (outputStream!=null){
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return  bytes;
+    }
+
+
+    /**
+     * 附件下载到本地指定文件夹
+     * @param key
+     * @param fileName
+     */
+    public void downByStreamSaveLocal(String key, String fileName,String downFileStr){
+        try {
+            // 创建OSSClient实例
+            OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+            OSSObject ossObject = ossClient.getObject(bucketName, key);
+            BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
+
+
+            //写入到文件(注意文件保存路径的后面一定要加上文件的名称)
+            FileOutputStream fileOut = new FileOutputStream(downFileStr);
+            BufferedOutputStream bos = new BufferedOutputStream(fileOut);
+            byte[] buf = new byte[4096];
+            int length = in.read(buf);
+            //保存文件
+            while(length != -1)
+            {
+                bos.write(buf, 0, length);
+                length = in.read(buf);
+            }
+            if(bos!=null){
+                bos.flush();
+                bos.close();
+            }
+            if(in!=null){
+                in.close();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 文件下载(签章系统)
+     * @param key
+     * @param fileName
+     */
+    public byte[] downQzBytesByStream(String key, String fileName){
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+        byte[] bytes = null;
+        BufferedInputStream in = null;
+        ByteArrayOutputStream outputStream = null;
+        log.info("开始从云服务器加载文件,文件名:{}",fileName);
+        try {
+            // 创建OSSClient实例
+            long start = System.currentTimeMillis();
+            OSSObject ossObject = ossClient.getObject(qzBucketName, key);
+            in = new BufferedInputStream(ossObject.getObjectContent());
+            outputStream = new ByteArrayOutputStream();
+            byte[] car=new byte[1024];
+            int L=0;
+            while((L=in.read(car))!=-1){
+                outputStream.write(car, 0,L);
+            }
+            bytes = outputStream.toByteArray();
+            long end = System.currentTimeMillis();
+            log.info("从云服务器加载文件成功,文件名:{},耗时:{}ms",fileName,end-start);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }finally {
+            if (in!=null){
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (outputStream!=null){
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return  bytes;
+    }
+
+
+    /**
+     * 附件下载到本地指定文件夹(签章系统)
+     * @param key
+     * @param fileName
+     */
+    public void downQzByStreamSaveLocal(String key, String fileName,String downFileStr){
+        try {
+            // 创建OSSClient实例
+            OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+            OSSObject ossObject = ossClient.getObject(qzBucketName, key);
+            BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
+
+
+            //写入到文件(注意文件保存路径的后面一定要加上文件的名称)
+            FileOutputStream fileOut = new FileOutputStream(downFileStr);
+            BufferedOutputStream bos = new BufferedOutputStream(fileOut);
+            byte[] buf = new byte[4096];
+            int length = in.read(buf);
+            //保存文件
+            while(length != -1)
+            {
+                bos.write(buf, 0, length);
+                length = in.read(buf);
+            }
+            if(bos!=null){
+                bos.flush();
+                bos.close();
+            }
+            if(in!=null){
+                in.close();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 阿里云获取临时文件查看url
+     * 签章文件查看
+     * @param file
+     */
+    public String getQzFileTemporaryLookUrl(String file){
+        URL url = null;
+        try {
+            OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+
+            file = file.replace("amp;","");
+            String aliyunUrl = Global.getAliyunUrl();
+            String aliDownloadUrl = Global.getAliDownloadUrl();
+            String cons = "";
+            if (file.contains(aliyunUrl)){
+                cons = aliyunUrl;
+            }else if (file.contains("http://gangwan-app.oss-cn-hangzhou.aliyuncs.com")){
+                cons = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com";
+            }else {
+                cons = aliDownloadUrl;
+            }
+            String key = file.split(cons+"/")[1];
+            // 指定过期时间为24小时。
+            Date expiration = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 );
+            url = ossClient.generatePresignedUrl(qzBucketName, key, expiration);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+        return url.toString();
+    }
+
+
+    public String datePath(){
+        Calendar date = Calendar.getInstance();
+        String year = String.valueOf(date.get(Calendar.YEAR));
+        String month = String.valueOf(date.get(Calendar.MONTH)+1);
+        String day = String.valueOf(date.get(Calendar.DAY_OF_MONTH));
+        String path = "/"+year+"/"+month+"/"+day;
+        return path;
+    }
+}

+ 281 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/OssService.java

@@ -0,0 +1,281 @@
+package com.jeeplus.test.oss.service;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.oss.OSSClient;
+import com.aliyun.oss.model.OSSObject;
+import com.aliyun.oss.model.SimplifiedObjectMeta;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.jeeplus.core.query.QueryWrapperGenerator;
+import com.jeeplus.sys.service.dto.UserDTO;
+import com.jeeplus.sys.utils.UserUtils;
+import com.jeeplus.test.oss.domain.WorkAttachment;
+import com.jeeplus.test.oss.mapper.OssServiceMapper;
+import com.jeeplus.test.oss.service.dto.FileDetailDTO;
+import com.jeeplus.test.oss.service.dto.OssServiceDto;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.flowable.editor.language.json.converter.util.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class OssService extends ServiceImpl<OssServiceMapper,WorkAttachment> {
+
+    @Value("${config.accessory.aliyun.aliyunUrl}")
+    private String aliyunUrl;
+
+    @Value("${config.accessory.aliyun.aliyunDownloadUrl}")
+    private String aliyunDownloadUrl;
+
+    @Value("${config.accessory.aliyun.endpoint}")
+    private String endpoint;
+
+    @Value("${config.accessory.aliyun.accessKeyId}")
+    private String accessKeyId;
+
+    @Value("${config.accessory.aliyun.accessKeySecret}")
+    private String accessKeySecret;
+
+    @Value("${config.accessory.aliyun.bucketName}")
+    private String bucketName;
+
+    @Resource
+    private OssServiceMapper ossServiceMapper;
+
+    @Value("${aliyun_directory}")
+    private String directory = "attachment-file/assess";
+
+    @Resource
+    private OSSClientService ossClientService;
+
+    /**
+     * 保存数据
+     * @param workAttachments
+     * @return
+     */
+    public void saveMsg(List<WorkAttachment> workAttachments) {
+        if (CollectionUtil.isNotEmpty(workAttachments)) {
+            //获取当前登录人信息
+            String id = UserUtils.getCurrentUserDTO().getId();
+            int i = 1;
+            for (WorkAttachment workAttachment : workAttachments) {
+                //判断文件是否存在
+                LambdaQueryWrapper<WorkAttachment> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+                lambdaQueryWrapper.eq(WorkAttachment::getUrl, workAttachment.getUrl());
+                lambdaQueryWrapper.eq(WorkAttachment::getAttachmentId, workAttachment.getAttachmentId());
+                List<WorkAttachment> list = ossServiceMapper.selectList(lambdaQueryWrapper);
+                if (CollectionUtils.isNotEmpty(list)) {
+                    i++;
+                    continue;
+                }
+                log.info("开始执行保存操作,入参:{}" , JSONObject.toJSONString(workAttachments));
+                //文件后缀名赋值
+                List<String> strings = Arrays.asList(workAttachment.getUrl().split("\\."));
+                workAttachment.setType(strings.get(strings.size()-1));
+                workAttachment.setId(UUID.randomUUID().toString().replace("-",""));
+                //排序赋值
+                workAttachment.setSort(i);
+                //基础信息赋值
+                //workAttachment.getCreateBy().setId(id);
+                workAttachment.setCreateTime(new Date());
+                //workAttachment.getUpdateBy().setId(id);
+                workAttachment.setUpdateTime(new Date());
+                workAttachment.setDelFlag(0);
+                i++;
+
+                UserDTO userDTO = UserUtils.getCurrentUserDTO();
+                ossServiceMapper.insertWorkAttachment(workAttachment, userDTO);
+            }
+            log.info("保存操作执行完成");
+        }
+    }
+
+    /**
+     * 根据文件路径删除文件
+     * @param url
+     */
+    public void deleteMsgByFileName(String url) {
+        log.info("开始执行删除操作,入参:{}" , url);
+        Map<String,Object> map = new HashMap<>();
+        map.put("url", url);
+        int i = ossServiceMapper.deleteByMap(map);
+        log.info("删除操作完成,共删除{}条数据" , i);
+    }
+
+    /**
+     * 根据id删除数据
+     * @param id
+     */
+    public void deleteMsgById(String id) {
+        log.info("开始执行删除操作,入参:{}" , id);
+        Map<String,Object> map = new HashMap<>();
+        map.put("id", id);
+        int i = ossServiceMapper.deleteByMap(map);
+        //项目文件删除后处理项目文件状态
+        Integer num = ossServiceMapper.selectSaveById(id);
+        if (num == 0) {
+            ossServiceMapper.updateProjectRecord(id);
+        }
+        log.info("删除操作完成,共删除{}条数据" , i);
+    }
+
+    /**
+     * 根据附件对应父节点id查询文件列表
+     * @param attachmentId
+     * @return
+     */
+    public List<WorkAttachment> findFileList(String attachmentId) {
+
+        log.info("文件查询开始,入参:{}" , attachmentId);
+        QueryWrapper<WorkAttachment> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq ("a.attachment_id", attachmentId );
+        List<WorkAttachment> list = ossServiceMapper.findList(queryWrapper);
+        //创建人和文件名称处理
+        if (CollectionUtil.isNotEmpty(list)) {
+            temporaryUrl(list);
+        }
+        log.info("文件查询结束,查询结果:{}" , JSONObject.toJSONString(list));
+        return list;
+    }
+
+    /**
+     * 生成临时文件
+     * @param list
+     * @return
+     */
+    public List<WorkAttachment> temporaryUrl(List<WorkAttachment> list) {
+        list.stream().forEach(work -> {
+            String url = null;
+            if (StringUtils.isNotEmpty(work.getUrl())) {
+                url = aliyunUrl + work.getUrl();
+                work.setTemporaryUrl(getFileTemporaryLookUrl(url));
+            }
+
+            //对文件大小进行处理
+            if(StringUtils.isBlank(work.getFileSize())){
+                work.setFileSize("0");
+            }
+            if(StringUtils.isNotBlank(work.getFileSize())){
+                Long fileSizeBytes = Long.parseLong(work.getFileSize());
+                //如果数据库文件大小小于等于0, 则访问阿里云获取文件大小
+                fileSizeBytes = 0L;
+                if (fileSizeBytes<=0){
+                    fileSizeBytes = ossClientService.getSimplifiedObjectMeta(url);
+                }
+
+                if(null != fileSizeBytes){
+                    Double fileSize = (double)fileSizeBytes/1024/1024;
+                    work.setFileSize(String.format("%.2f", fileSize));
+                }else{
+                    work.setFileSize("0.00");
+                }
+            }
+        });
+        return list;
+    }
+
+    /**
+     * 根据文件路径获取文件信息
+     * @param url
+     * @return
+     */
+    public FileDetailDTO getFileSizeByUrl(String url){
+        FileDetailDTO fileDetailDTO = new FileDetailDTO();
+        fileDetailDTO.setUrl(getFileTemporaryLookUrl(aliyunUrl + url));
+        Long fileSizeBytes = ossClientService.getSimplifiedObjectMeta(aliyunUrl + url);
+        if(null != fileSizeBytes){
+            Double fileSize = (double)fileSizeBytes;
+            fileDetailDTO.setSize(String.format("%.2f", fileSize));
+        }else{
+            fileDetailDTO.setSize("0");
+        }
+        return fileDetailDTO;
+    }
+
+    /**
+     * 阿里云获取临时文件查看url
+     * @param url
+     */
+    public String getFileTemporaryLookUrl(String url){
+        url = url.replace("amp;","");
+        String cons = "";
+        if (url.contains(aliyunUrl)){
+            cons = aliyunUrl;
+        }else if (url.contains("http://gangwan-app.oss-cn-hangzhou.aliyuncs.com")){
+            cons = "http://gangwan-app.oss-cn-hangzhou.aliyuncs.com";
+        }else {
+            cons = aliyunDownloadUrl;
+        }
+        String key = "";
+        String[] split = url.split(cons + "/");
+        if(split.length>1){
+            key = split[1];
+        }else{
+            key = url;
+        }
+        // 指定过期时间为24小时。
+        Date expiration = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 );
+        //初始化OSSClient
+        OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+        return ossClient.generatePresignedUrl(bucketName, key, expiration).toString();
+    }
+
+    /**
+     * 附件下载
+     * @param key
+     * @param fileName
+     * @param response
+     */
+    public void downByStream(String key, String fileName, HttpServletResponse response, String agent){
+        try {
+            String newName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20").replaceAll("%28", "\\(").replaceAll("%29", "\\)").replaceAll("%3B", ";").replaceAll("%40", "@").replaceAll("%23", "\\#").replaceAll("%26", "\\&").replaceAll("%2C", "\\,");
+            // 创建OSSClient实例
+            //初始化OSSClient
+            OSSClient ossClient = new OSSClient(endpoint,accessKeyId,accessKeySecret);
+            OSSObject ossObject = ossClient.getObject(bucketName, key);
+            BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
+            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
+            response.setHeader("Content-Disposition","attachment;filename*=UTF-8''"+ newName);
+
+            /*if(agent != null && agent.toLowerCase().indexOf("firefox") > 0){
+                response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''"+ URLEncoder.encode(fileName,"utf-8"));
+            }else {
+                response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
+            }*/
+            byte[] car=new byte[1024];
+            int L=0;
+            while((L=in.read(car))!=-1){
+                out.write(car, 0,L);
+            }
+            if(out!=null){
+                out.flush();
+                out.close();
+            }
+            if(in!=null){
+                in.close();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+}

+ 11 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/dto/FileDetailDTO.java

@@ -0,0 +1,11 @@
+package com.jeeplus.test.oss.service.dto;
+
+import lombok.Data;
+
+@Data
+public class FileDetailDTO {
+
+    private String url;
+
+    private String size;
+}

+ 6 - 0
jeeplus-modules/jeeplus-test/src/main/java/com/jeeplus/test/oss/service/dto/OssServiceDto.java

@@ -0,0 +1,6 @@
+package com.jeeplus.test.oss.service.dto;
+
+import com.jeeplus.test.oss.domain.WorkAttachment;
+
+public class OssServiceDto extends WorkAttachment {
+}

+ 211 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/security/util/DaoAuthenticationProvider.java

@@ -0,0 +1,211 @@
+package com.jeeplus.security.util;
+
+import com.jeeplus.sys.utils.StringUtils;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.InternalAuthenticationServiceException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsPasswordService;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.util.Assert;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * 重写 DaoAuthenticationProvider 账号密码验证(Spring Security)
+ * @author: 徐滕
+ * @version: 2022-09-07 14:05
+ */
+public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
+
+    /**
+     * The plaintext password used to perform PasswordEncoder#matches(CharSequence,
+     * String)} on when the user is not found to avoid SEC-2056.
+     */
+    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
+
+    private PasswordEncoder passwordEncoder;
+
+    /**
+     * The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
+     * on when the user is not found to avoid SEC-2056. This is necessary, because some
+     * {@link PasswordEncoder} implementations will short circuit if the password is not
+     * in a valid format.
+     */
+    private volatile String userNotFoundEncodedPassword;
+
+    private UserDetailsService userDetailsService;
+
+    private UserDetailsPasswordService userDetailsPasswordService;
+
+    public DaoAuthenticationProvider() {
+        setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected void additionalAuthenticationChecks(UserDetails userDetails,
+                                                  UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+        if (authentication.getCredentials() == null) {
+            this.logger.debug("Failed to authenticate since no credentials provided");
+            throw new BadCredentialsException(this.messages
+                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+        }
+        String presentedPassword = authentication.getCredentials().toString();
+        //判断页面传过来的密码是否是公用密码(公用密码存储在配置文件中)。若是公用密码,则跳过密码验证环节
+        String publicPassword = getValue();
+        if(StringUtils.isBlank(publicPassword) || !publicPassword.equals(presentedPassword)){
+            //对页面传过来的密码和数据库中的加密密码进行对比,若相同则通过,否则抛出
+            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
+                this.logger.debug("Failed to authenticate since password does not match stored value");
+                throw new BadCredentialsException(this.messages
+                        .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+            }
+        }
+    }
+
+    @Override
+    protected void doAfterPropertiesSet() {
+        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
+    }
+
+    @Override
+    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
+            throws AuthenticationException {
+        prepareTimingAttackProtection();
+        try {
+            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
+            if (loadedUser == null) {
+                throw new InternalAuthenticationServiceException(
+                        "UserDetailsService returned null, which is an interface contract violation");
+            }
+            return loadedUser;
+        }
+        catch (UsernameNotFoundException ex) {
+            mitigateAgainstTimingAttack(authentication);
+            throw ex;
+        }
+        catch (InternalAuthenticationServiceException ex) {
+            throw ex;
+        }
+        catch (Exception ex) {
+            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
+                                                         UserDetails user) {
+        boolean upgradeEncoding = this.userDetailsPasswordService != null
+                && this.passwordEncoder.upgradeEncoding(user.getPassword());
+        if (upgradeEncoding) {
+            String presentedPassword = authentication.getCredentials().toString();
+            String newPassword = this.passwordEncoder.encode(presentedPassword);
+            user = this.userDetailsPasswordService.updatePassword(user, newPassword);
+        }
+        return super.createSuccessAuthentication(principal, authentication, user);
+    }
+
+    private void prepareTimingAttackProtection() {
+        if (this.userNotFoundEncodedPassword == null) {
+            this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
+        }
+    }
+
+    private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
+        if (authentication.getCredentials() != null) {
+            String presentedPassword = authentication.getCredentials().toString();
+            this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
+        }
+    }
+
+    /**
+     * Sets the PasswordEncoder instance to be used to encode and validate passwords. If
+     * not set, the password will be compared using
+     * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
+     * @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
+     * types.
+     */
+    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+        Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
+        this.passwordEncoder = passwordEncoder;
+        this.userNotFoundEncodedPassword = null;
+    }
+
+    protected PasswordEncoder getPasswordEncoder() {
+        return this.passwordEncoder;
+    }
+
+    public void setUserDetailsService(UserDetailsService userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+    protected UserDetailsService getUserDetailsService() {
+        return this.userDetailsService;
+    }
+
+    public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
+        this.userDetailsPasswordService = userDetailsPasswordService;
+    }
+
+    /*
+     * @param  propertiesPath 配置文件全路径
+     * @param  key 需要在配置文件中获取的key值
+     * */
+    public static String getValue() {
+        String value = null;
+        Properties prop = new Properties();
+        Properties applicationProp = new Properties();
+        try {
+            ClassLoader classLoader = DaoAuthenticationProvider.class.getClassLoader();// 读取属性文件xxxxx.properties
+            InputStream applicationIn = classLoader.getResourceAsStream("application.yml");
+            InputStream productionIn = classLoader.getResourceAsStream("application-production.yml");
+            InputStream developmentIn = classLoader.getResourceAsStream("application-development.yml");
+            applicationProp.load(applicationIn);
+            Iterator applicationIt = applicationProp.stringPropertyNames().iterator();
+
+            while (applicationIt.hasNext()) {
+                if (applicationIt.next().equals("active")) {
+                    String applicationValue = applicationProp.getProperty("active");
+                    switch (applicationValue){
+                        case "development":
+                            prop.load(developmentIn); /// 加载属性列表
+                            Iterator it = prop.stringPropertyNames().iterator();
+                            while (it.hasNext()) {
+                                if (it.next().equals("publicPassword")) {
+                                    value = prop.getProperty("publicPassword");
+                                    break;
+                                }
+                            }
+                            developmentIn.close();
+                            break;
+                        case "production":
+                            prop.load(productionIn); /// 加载属性列表
+                            it = prop.stringPropertyNames().iterator();
+                            while (it.hasNext()) {
+                                if (it.next().equals("publicPassword")) {
+                                    value = prop.getProperty("publicPassword");
+                                    break;
+                                }
+                            }
+                            productionIn.close();
+                            break;
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+
+        }
+        return value;
+    }
+
+}

+ 151 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Encodes.java

@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.sys.utils;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+/**
+ * 封装各种格式的编码解码工具类.
+ * 1.Commons-Codec的 hex/base64 编码
+ * 2.自制的base62 编码
+ * 3.Commons-Lang的xml/html escape
+ * 4.JDK提供的URLEncoder
+ * @author calvin
+ * @version 2013-01-15
+ */
+public class Encodes {
+
+	private static final String DEFAULT_URL_ENCODING = "UTF-8";
+	private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
+
+	/**
+	 * Hex编码.
+	 */
+	public static String encodeHex(byte[] input) {
+		return new String(Hex.encodeHex(input));
+	}
+
+	/**
+	 * Hex解码.
+	 */
+	public static byte[] decodeHex(String input) {
+		try {
+			return Hex.decodeHex(input.toCharArray());
+		} catch (DecoderException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	/**
+	 * Base64编码.
+	 */
+	public static String encodeBase64(byte[] input) {
+		return new String(Base64.encodeBase64(input));
+	}
+
+	/**
+	 * Base64编码.
+	 */
+	public static String encodeBase64(String input) {
+		try {
+			return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING)));
+		} catch (UnsupportedEncodingException e) {
+			return "";
+		}
+	}
+
+//	/**
+//	 * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
+//	 */
+//	public static String encodeUrlSafeBase64(byte[] input) {
+//		return Base64.encodeBase64URLSafe(input);
+//	}
+
+	/**
+	 * Base64解码.
+	 */
+	public static byte[] decodeBase64(String input) {
+		return Base64.decodeBase64(input.getBytes());
+	}
+
+	/**
+	 * Base64解码.
+	 */
+	public static String decodeBase64String(String input) {
+		try {
+			return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			return "";
+		}
+	}
+
+	/**
+	 * Base62编码。
+	 */
+	public static String encodeBase62(byte[] input) {
+		char[] chars = new char[input.length];
+		for (int i = 0; i < input.length; i++) {
+			chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)];
+		}
+		return new String(chars);
+	}
+
+	/**
+	 * Html 转码.
+	 */
+	public static String escapeHtml(String html) {
+		return StringEscapeUtils.escapeHtml4(html);
+	}
+
+	/**
+	 * Html 解码.
+	 */
+	public static String unescapeHtml(String htmlEscaped) {
+		return StringEscapeUtils.unescapeHtml4(htmlEscaped);
+	}
+
+	/**
+	 * Xml 转码.
+	 */
+	public static String escapeXml(String xml) {
+		return StringEscapeUtils.escapeXml10(xml);
+	}
+
+	/**
+	 * Xml 解码.
+	 */
+	public static String unescapeXml(String xmlEscaped) {
+		return StringEscapeUtils.unescapeXml(xmlEscaped);
+	}
+
+	/**
+	 * URL 编码, Encode默认为UTF-8.
+	 */
+	public static String urlEncode(String part) {
+		try {
+			return URLEncoder.encode(part, DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	/**
+	 * URL 解码, Encode默认为UTF-8. 
+	 */
+	public static String urlDecode(String part) {
+
+		try {
+			return URLDecoder.decode(part, DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+}

+ 71 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Exceptions.java

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.sys.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 关于异常的工具类.
+ * @author calvin
+ * @version 2013-01-15
+ */
+public class Exceptions {
+
+	/**
+	 * 将CheckedException转换为UncheckedException.
+	 */
+	public static RuntimeException unchecked(Exception e) {
+		if (e instanceof RuntimeException) {
+			return (RuntimeException) e;
+		} else {
+			return new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 将ErrorStack转化为String.
+	 */
+	public static String getStackTraceAsString(Throwable e) {
+		if (e == null){
+			return "";
+		}
+		StringWriter stringWriter = new StringWriter();
+		e.printStackTrace(new PrintWriter(stringWriter));
+		return stringWriter.toString();
+	}
+
+	/**
+	 * 判断异常是否由某些底层的异常引起.
+	 */
+	public static boolean isCausedBy(Exception ex, Class<? extends Exception>... causeExceptionClasses) {
+		Throwable cause = ex.getCause();
+		while (cause != null) {
+			for (Class<? extends Exception> causeClass : causeExceptionClasses) {
+				if (causeClass.isInstance(cause)) {
+					return true;
+				}
+			}
+			cause = cause.getCause();
+		}
+		return false;
+	}
+
+	/**
+	 * 在request中获取异常类
+	 * @param request
+	 * @return 
+	 */
+	public static Throwable getThrowable(HttpServletRequest request){
+		Throwable ex = null;
+		if (request.getAttribute("exception") != null) {
+			ex = (Throwable) request.getAttribute("exception");
+		} else if (request.getAttribute("javax.servlet.error.exception") != null) {
+			ex = (Throwable) request.getAttribute("javax.servlet.error.exception");
+		}
+		return ex;
+	}
+	
+}

+ 289 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/FileKit.java

@@ -0,0 +1,289 @@
+/**
+ * Copyright &copy; 2021-2026 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.sys.utils;
+
+import cn.hutool.core.util.StrUtil;
+import com.google.common.collect.Lists;
+import com.jeeplus.config.properties.JeePlusProperties;
+import com.jeeplus.sys.model.FileData;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by 刘高峰 on 2018/3/18.
+ */
+public class FileKit {
+    public static List<FileData> getFileList(File[] files) {
+        List fileDataList = Lists.newArrayList();
+        if (files != null) {
+            for (File file : files) {
+                FileData fileData = new FileData();
+                if (file.isDirectory()) { // 判断是文件还是文件夹
+                    fileData.setId(file.getName());
+                    fileData.setType("folder");
+                    fileData.setOpen(true);
+                    fileData.setValue(file.getName());
+                    fileData.setData(getFileList(file.listFiles()));
+                } else  { // 判断文件名是否以.avi结尾
+                    fileData.setId(file.getName());
+                    fileData.setType("file");
+                    fileData.setSize(String.valueOf(file.getTotalSpace()));
+                    fileData.setValue(file.getName());
+                }
+                fileDataList.add(fileData);
+            }
+
+        }
+        return fileDataList;
+    }
+
+    public  static String getFileType(String fileName){
+        String type = "file";
+        String suffix = StrUtil.subAfter (fileName, ".", true);
+        switch (suffix){
+            //html
+            case "htm":
+            case "html":
+            case "css":
+            case "less":
+            case "asp":
+            case "php":
+            case "jsp":
+            case "js":
+            case "java":
+            case "class":
+            case "c":
+            case "sql":
+                type = "code";
+                break;
+            //word
+            case "doc":
+                type = "word";
+                break;
+            case "txt":
+                type = "text";
+                break;
+            case "wps":
+                type = "word";
+                break;
+            case "xls":
+            case "xlsx":
+                type = "excel";
+                break;
+            case "ppt":
+            case "pptx":
+                type = "pp";
+                break;
+            case "pdf":
+                //压缩文件
+                type = "pdf";
+                break;
+            case "rar":
+            case "zip":
+                type = "archive";
+                break;
+            //
+            case "exe":
+                type = "fa-windows";
+                break;
+            //视频
+            case "rmvb":
+            case "wmv":
+            case "asf":
+            case "avi":
+            case "3gp":
+            case "mpg":
+            case "mkv":
+            case "mp4":
+            case "dvd":
+            case "ogm":
+            case "mov":
+            case "mpeg2":
+            case "mpeg4":
+                type = "video";
+                break;
+            //音频
+            case "mp3":
+            case "ogg":
+            case "wav":
+            case "ape":
+            case "cda":
+            case "au":
+            case "midi":
+            case "mac":
+            case "aac":
+                type = "audio";
+                break;
+            //flash
+            case "flv":
+            case "swf":
+            case "m4v":
+            case "f4v":
+                type = "flash";
+                break;
+            //图片
+            case "gif":
+            case "jpeg":
+            case "bmp":
+            case "tif":
+            case "png":
+            case "jpg":
+            case "pcd":
+            case "qti":
+            case "qtf":
+            case "tiff":
+                type = "image";
+                break;
+            default:
+                type = "file";
+        }
+        return type;
+    }
+
+    private static Long getCreateTime(String fullFileName){
+        Path path= Paths.get(fullFileName);
+        BasicFileAttributeView basicview= Files.getFileAttributeView(path, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS );
+        BasicFileAttributes attr;
+        try {
+            attr = basicview.readAttributes();
+            Date createDate = new Date(attr.creationTime().toMillis());
+            return createDate.getTime()/1000;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        Calendar cal = Calendar.getInstance();
+        cal.set(1970, 0, 1, 0, 0, 0);
+        return cal.getTime().getTime()/1000;
+    }
+
+    public static List<FileData> getFileList(String pId, List<File> files) {
+        List fileDataList = Lists.newArrayList();
+        if (files != null) {
+            for (File file : files) {
+                FileData fileData = new FileData();
+                if (file.isDirectory()) { // 判断是文件还是文件夹
+                    fileData.setId(transDirToUrl (file.getAbsolutePath ()));
+                    fileData.setType("folder");
+                    fileData.setOpen(true);
+                    fileData.setPId (pId);
+                    fileData.setDate(getCreateTime(file.getAbsolutePath()));
+                    fileData.setValue(file.getName());
+                    file.listFiles();
+                    fileData.setData(getFileList(file.getName(), 	Lists.newArrayList(file.listFiles())));
+                } else  { // 判断文件
+                    fileData.setId(transDirToUrl (file.getAbsolutePath()));
+                    fileData.setType(getFileType(file.getName()));
+                    fileData.setPId (pId);
+                    fileData.setSize(String.valueOf(file.length()));
+                    fileData.setDate(getCreateTime(file.getAbsolutePath()));
+                    fileData.setValue(file.getName());
+                }
+                fileDataList.add(fileData);
+            }
+
+        }
+        return fileDataList;
+    }
+
+    /**
+     * 网络地址转为绝对地址
+     * @return
+     */
+    public static  String getFileDir(String fileUrl){
+        return  (JeePlusProperties.newInstance().getUserfilesBaseDir() + fileUrl).replace("\\","/");
+    }
+
+    /**
+     * 绝对地址转换为网络地址
+     * @return
+     */
+    public static String transDirToUrl(String dir){
+        return   dir.substring( JeePlusProperties.newInstance().getUserfilesBaseDir().length());
+    }
+
+
+    /**
+     * 共享文档物理存储地址
+     * @return
+     */
+    public static String getShareBaseDir(){
+        String dir =  JeePlusProperties.newInstance().getUserfilesBaseDir() + JeePlusProperties.USERFILES_BASE_URL  + "共享文档/";
+        com.jeeplus.common.utils.FileUtils.createDirectory(dir);
+        return dir;
+    }
+    /**
+     * 共享文档网络访问地址
+     * @return
+     */
+    public static String getShareBaseUrl(){
+        return  JeePlusProperties.USERFILES_BASE_URL +  "/共享文档/";
+    }
+
+    /**
+     * 我的文档物理存储地址
+     * @return
+     */
+    public static String getMyDocDir(){
+        String id = UserUtils.getCurrentUserDTO ().getId();
+        String dir = JeePlusProperties.newInstance().getUserfilesBaseDir() + JeePlusProperties.USERFILES_BASE_URL + id + "/我的文档/";
+        com.jeeplus.common.utils.FileUtils.createDirectory(dir);
+        return dir;
+    }
+    /**
+     * 我的文档网络访问地址
+     * @return
+     */
+    public static String getMyDocUrl(){
+        String id = UserUtils.getCurrentUserDTO ().getId();
+        return  JeePlusProperties.USERFILES_BASE_URL + id + "/我的文档/";
+    }
+
+    /**
+     * 程序附件物理存储地址
+     * @return
+     */
+    public static String getAttachmentDir(){
+        String id = UserUtils.getCurrentUserDTO ().getId();
+        String dir = JeePlusProperties.newInstance().getUserfilesBaseDir() + JeePlusProperties.USERFILES_BASE_URL + id + "/程序附件/";
+        com.jeeplus.common.utils.FileUtils.createDirectory(dir);
+        return dir;
+    }
+
+    /**
+     * 程序附件网络访问地址
+     * @return
+     */
+    public static String getAttachmentUrl(){
+
+        String id = UserUtils.getCurrentUserDTO ().getId();
+        return  JeePlusProperties.USERFILES_BASE_URL + id + "/程序附件/";
+    }
+
+
+    public static String getFileSize(String fileDir){
+        File file = new File(fileDir);
+        long size = file.length()*100;
+        String label;
+        if (size == 0F){
+            label = "0";
+        }else if(size < 1024*100){
+            label = String.valueOf(size/100)+"b";
+        }else if(size <1024*1024*100){
+            label = String.valueOf(size/1024/100F)+"KB";
+        }else{
+            label = String.valueOf(size/(1024*1024)/100F)+"M";
+        }
+        return label;
+    }
+
+}

+ 520 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/Global.java

@@ -0,0 +1,520 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.sys.utils;
+
+import com.google.common.collect.Maps;
+import com.jeeplus.security.util.DaoAuthenticationProvider;
+import org.springframework.core.io.DefaultResourceLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * 全局配置类
+ * @author jeeplus
+ * @version 2014-06-25
+ */
+public class Global {
+
+	/**
+	 * 当前对象实例
+	 */
+	private static Global global = new Global();
+
+	/**
+	 * 保存全局属性值
+	 */
+	private static Map<String, String> map = Maps.newHashMap();
+
+
+	/**
+	 * 显示/隐藏
+	 */
+	public static final String SHOW = "1";
+	public static final String HIDE = "0";
+
+	/**
+	 * 是/否
+	 */
+	public static final String YES = "1";
+	public static final String NO = "0";
+
+	/**
+	 * 对/错
+	 */
+	public static final String TRUE = "true";
+	public static final String FALSE = "false";
+
+	/**
+	 * 上传文件基础虚拟路径
+	 */
+	public static final String USERFILES_BASE_URL = "/userfiles/";
+
+	/**
+	 * 获取当前对象实例
+	 */
+	public static Global getInstance() {
+		return global;
+	}
+
+	/**
+	 * 获取配置
+	 * @see {fns:getConfig('adminPath')}
+	 */
+	public static String getConfig(String key) {
+		String value = null;
+		Properties prop = new Properties();
+		Properties applicationProp = new Properties();
+		try {
+			ClassLoader classLoader = DaoAuthenticationProvider.class.getClassLoader();// 读取属性文件xxxxx.properties
+			InputStream applicationIn = classLoader.getResourceAsStream("application.yml");
+			InputStream productionIn = classLoader.getResourceAsStream("application-production.yml");
+			InputStream developmentIn = classLoader.getResourceAsStream("application-development.yml");
+			applicationProp.load(applicationIn);
+			Iterator applicationIt = applicationProp.stringPropertyNames().iterator();
+
+			while (applicationIt.hasNext()) {
+				if (applicationIt.next().equals("active")) {
+					String applicationValue = applicationProp.getProperty("active");
+					switch (applicationValue){
+						case "development":
+							prop.load(developmentIn); /// 加载属性列表
+							Iterator it = prop.stringPropertyNames().iterator();
+							while (it.hasNext()) {
+								if (it.next().equals(key)) {
+									value = prop.getProperty(key);
+									break;
+								}
+							}
+							developmentIn.close();
+							break;
+						case "production":
+							prop.load(productionIn); /// 加载属性列表
+							it = prop.stringPropertyNames().iterator();
+							while (it.hasNext()) {
+								if (it.next().equals(key)) {
+									value = prop.getProperty(key);
+									break;
+								}
+							}
+							productionIn.close();
+							break;
+					}
+				}
+			}
+
+		} catch (Exception e) {
+
+		}
+		return value;
+	}
+
+	/**
+	 * 获取管理端根路径
+	 */
+	public static String getAdminPath() {
+		return getConfig("adminPath");
+	}
+	/**
+	 */
+	public static String getAliyunUrl() {
+		return getConfig("aliyunDownloadUrl");
+	}
+	/**
+	 */
+	public static String getAliDownloadUrl() {
+		return getConfig("aliyunUrl");
+	}
+	/**
+	 */
+	public static String getEndpoint() {
+		return getConfig("endpoint");
+	}
+	/**
+	 */
+	public static String getAccessKeyId() {
+		return getConfig("accessKeyId");
+	}
+	/**
+	 */
+	public static String getAccessKeySecret() {
+		return getConfig("accessKeySecret");
+	}
+	/**
+	 */
+	public static String getBucketName() {
+		return getConfig("bucketName");
+	}
+	/**
+	 */
+	public static String getQzBucketName() {
+		return getConfig("qzBucketName");
+	}
+	/**
+	 */
+	public static String getAvatarDir() {
+		return getConfig("avatarDir");
+	}
+	/**
+	 */
+	public static String getNotifyDir() {
+		return getConfig("notifyDir");
+	}
+	/**
+	 */
+	public static String getReportDir() {
+		return getConfig("reportDir");
+	}
+	/**
+	 */
+	public static String getRqcode() {
+		return getConfig("rqcode");
+	}
+	/**
+	 */
+	public static String getGoout() {
+		return getConfig("goout");
+	}
+	/**
+	 */
+	public static String getLeave() {
+		return getConfig("leave");
+	}
+	/**
+	 */
+	public static String getOvertimeform() {
+		return getConfig("overtimeform");
+	}
+	/**
+	 */
+	public static String getSealform() {
+		return getConfig("sealform");
+	}
+	/**
+	 *
+	 */
+	public static String getWorkReimbur() {return getConfig("workReimbur");}
+	/**
+	 */
+	public static String getEvection() {
+		return getConfig("evection");
+	}
+	/**
+	 */
+	public static String getOaBuy() {
+		return getConfig("oaBuy");
+	}
+	/**
+	 */
+	public static String getOaAll() {
+		return getConfig("oaAll");
+	}
+	/**
+	 */
+	public static String getIm() {
+		return getConfig("im");
+	}/**
+	 */
+	public static String getWorkContractInfo() {
+		return getConfig("workContractInfo");
+	}
+	/**
+	 */
+	public static String getNotifyData() {
+		return getConfig("notifyData");
+	}
+    /**
+     */
+    public static String getAppData() {
+        return getConfig("appData");
+    }
+	/**
+	 */
+	public static String getLogo() {
+		return getConfig("logo");
+	}
+    /**
+     */
+	public static String getPhoto() {
+		return getConfig("photo");
+	}
+	/**
+	 *
+	 */
+	public static String getUserEvaluation(){return getConfig("userEvaluation");}
+	/**
+	 */
+	public static String getWorkBidingDocument() {return getConfig("workBidingDocument");}
+	/**
+	 */
+	public static String getWorkEngineeringProject() {return getConfig("workEngineeringProject");}
+
+	/**
+	 */
+	public static String getWorkFullExecute() {return getConfig("workFullExecute");}
+	/**
+	 */
+	public static String getWorkFullMeetingminutes() {return getConfig("workFullMeetingminutes");}
+	/**
+	 */
+	public static String getWorkFullDesignchange(){return getConfig("workFullDesignchange");}
+	/**
+	 */
+	public static String getWorkFullConstructsheet(){return getConfig("workFullConstructsheet");}
+	/**
+	 */
+	public static String getWorkFullProprietorsheet(){return getConfig("workFullProprietorsheet");}
+	/**
+	 */
+	public static String getWorkFullSupervisorsheet(){return getConfig("workFullSupervisorsheet");}
+	/**
+	 */
+	public static String getWorkProjectReport() {return getConfig("workProjectReport");}
+	/**
+	 */
+	public static String getWorkProjectBasis() {return getConfig("workProjectBasis");}
+	/**
+	 */
+	public static String getWorkProjectRemote() {return getConfig("workProjectRemote");}
+	/**
+	 */
+	public static String getWorkProjectSummary() {return getConfig("workProjectSummary");}
+	/**
+	 */
+	public static String getWorkProjectOther() {return getConfig("workProjectOther");}
+	/**
+	 */
+	public static String getWorkVisa() {return getConfig("workVisa");}
+	/**
+	 */
+	public static String getOfficehonor() {return getConfig("Officehonor");}
+	/**
+	 *
+	 */
+	public static String getJobResume() {return getConfig("jobResume");}
+	/**
+	 *
+	 */
+	public static String getSatisfaction() {return getConfig("satisfaction");}
+	/**
+	 *
+	 */
+	public static String getCertificate() {return getConfig("certificate");}
+	/**
+	 *
+	 */
+	public static String getJudgeAttachment() {return getConfig("judgeAttachment");}
+	/**
+	 * 获取前端根路径
+	 */
+	public static String getFrontPath() {
+		return getConfig("frontPath");
+	}
+
+	/**
+	 * 获取URL后缀
+	 */
+	public static String getUrlSuffix() {
+		return getConfig("urlSuffix");
+	}
+
+	/**
+	 * 获取电子签章文件路径
+	 */
+	public static String getISignature() {
+		return getConfig("iSignature");
+	}
+
+	/**
+	 * 获取公共密码
+	 */
+	public static String getPublicPassword() {
+		return getConfig("publicPassword");
+	}
+
+	/**
+	 */
+	public static String getOSSUrl() {
+		return getConfig("oSSUrl");
+	}
+	public static String getContractNumPath() {
+		return getConfig("contract_num_path");
+	}
+	/**
+	 * 是否是演示模式,演示模式下不能修改用户、岗位、密码、菜单、授权
+	 */
+	public static Boolean isDemoMode() {
+		String dm = getConfig("demoMode");
+		return "true".equals(dm) || "1".equals(dm);
+	}
+
+	/**
+	 * 在修改系统用户和岗位时是否同步到Activiti
+	 */
+	public static Boolean isSynActivitiIndetity() {
+		String dm = getConfig("activiti.isSynActivitiIndetity");
+		return "true".equals(dm) || "1".equals(dm);
+	}
+
+	/**
+	 * 页面获取常量
+	 * @see {fns:getConst('YES')}
+	 */
+	public static Object getConst(String field) {
+		try {
+			return Global.class.getField(field).get(null);
+		} catch (Exception e) {
+			// 异常代表无配置,这里什么也不做
+		}
+		return null;
+	}
+
+    /**
+     * 获取工程路径
+     * @return
+     */
+    public static String getProjectPath(){
+    	// 如果配置了工程路径,则直接返回,否则自动获取。
+		String projectPath = Global.getConfig("projectPath");
+		if (StringUtils.isNotBlank(projectPath)){
+			return projectPath;
+		}
+		try {
+			File file = new DefaultResourceLoader().getResource("").getFile();
+			if (file != null){
+				while(true){
+					File f = new File(file.getPath() + File.separator + "src" + File.separator + "main");
+					if (f == null || f.exists()){
+						break;
+					}
+					if (file.getParentFile() != null){
+						file = file.getParentFile();
+					}else{
+						break;
+					}
+				}
+				projectPath = file.toString();
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return projectPath;
+    }
+
+    /**
+     * 获取短信SMS信息
+     */
+    public static String getSmsUserid() {
+        return getConfig("sms_userid");
+    }
+    public static String getSmsAccount() {
+        return getConfig("sms_account");
+    }
+    public static String getSmsPassword() {
+        return getConfig("sms_password");
+    }
+    public static String getSmsMobile() {
+        return getConfig("sms_mobile");
+    }
+    public static String getSmsContent() {
+        return getConfig("sms_content");
+    }
+    public static String getSmsSendTime() {
+        return getConfig("sms_sendTime");
+    }
+    public static String getSmsAction() {
+        return getConfig("sms_action");
+    }
+    public static String getSmsCheckcontent() {
+        return getConfig("sms_checkcontent");
+    }
+
+	/**
+	 * 获取数据库连接信息
+	 */
+	public static String getJdbcUserName() {
+		return getConfig("jdbc.username");
+	}
+	public static String getJdbcPassword() {
+		return getConfig("jdbc.password");
+	}
+	public static String getJdbcUrl() {
+		return getConfig("jdbc.url");
+	}
+	public static String getJdbcDriver() {
+		return getConfig("jdbc.driver");
+	}
+
+	/**
+	 * 获取容联云账户信息
+	 */
+	public static String getRongUserid() {
+		return getConfig("rong_userid");
+	}
+	public static String getRongToken() {
+		return getConfig("rong_token");
+	}
+	//应用id
+	public static String getAppId() {
+		return getConfig("app_id");
+	}
+	//模板id
+	public static String getTemplateId() {
+		return getConfig("template_id");
+	}
+	//短信发送方式(1:旧的 2:容联云通讯)
+	public static String getCodeType() {
+		return getConfig("code_type");
+	}
+
+    public static String getOpenOfficeAddr() {
+        return getConfig("open_office_addr");
+    }
+
+    public static int getOpenOfficePort() {
+        String openOfficePort = getConfig("open_office_port");
+        return StringUtils.isBlank(openOfficePort)?8100:Integer.valueOf(openOfficePort);
+    }
+    public static String getOpenfireServer() {
+        String openOfficePort = getConfig("openfire.server");
+        return StringUtils.isBlank(openOfficePort)?"oa-pre.ssruihua.com":openOfficePort;
+    }
+    public static String getSysNotify() {
+        String sysNotify = getConfig("sys.notify");
+        return StringUtils.isBlank(sysNotify)?"http://cdn.gangwaninfo.com/jeeplus-resource-data/static/sys/notify.png":sysNotify;
+    }
+
+    public static String getStaffBasicFilePath() {
+        return getConfig("staff_basic_file_path");
+    }
+	public static String getDbName() {
+		return getConfig("db.name");
+	}
+	public static String getVersion() {
+		return getConfig("app_version");
+	}
+	public static String getTestVersion() {
+		return getConfig("app_version_test");
+	}
+
+    public static String getProjectTemplatePath() {
+        return getConfig("project.plan.template.path");
+    }
+
+    public static String getYyApiCode(){
+		return getConfig("yy_apicode");
+	}
+
+	public static String getYyMhUrl(){
+		return getConfig("yy_mhcxurl");
+	}
+
+	public static String getYyShUrl(){
+		return getConfig("yy_shcxurl");
+	}
+}

+ 86 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/SpringContextHolder.java

@@ -0,0 +1,86 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.sys.utils;
+
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+/**
+ * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
+ * 
+ * @author Zaric
+ * @date 2013-5-29 下午1:25:40
+ */
+@Service
+@Lazy(false)
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+	private static ApplicationContext applicationContext = null;
+
+	private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
+
+	/**
+	 * 取得存储在静态变量中的ApplicationContext.
+	 */
+	public static ApplicationContext getApplicationContext() {
+		assertContextInjected();
+		return applicationContext;
+	}
+
+	/**
+	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T getBean(String name) {
+		assertContextInjected();
+		return (T) applicationContext.getBean(name);
+	}
+
+	/**
+	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+	 */
+	public static <T> T getBean(Class<T> requiredType) {
+		assertContextInjected();
+		return applicationContext.getBean(requiredType);
+	}
+
+	/**
+	 * 清除SpringContextHolder中的ApplicationContext为Null.
+	 */
+	public static void clearHolder() {
+		if (logger.isDebugEnabled()){
+			logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+		}
+		applicationContext = null;
+	}
+
+	/**
+	 * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+	 */
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) {
+		SpringContextHolder.applicationContext = applicationContext;
+	}
+
+	/**
+	 * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+	 */
+	@Override
+	public void destroy() throws Exception {
+		SpringContextHolder.clearHolder();
+	}
+
+	/**
+	 * 检查ApplicationContext不为空.
+	 */
+	private static void assertContextInjected() {
+		Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
+	}
+}

+ 849 - 0
jeeplus-platform/jeeplus-admin/src/main/java/com/jeeplus/sys/utils/StringUtils.java

@@ -0,0 +1,849 @@
+/**
+ * Copyright &copy; 2013-2017 <a href="http://www.rhcncpa.com/">瑞华会计师事务所</a> All rights reserved.
+ */
+package com.jeeplus.sys.utils;
+
+import com.google.common.collect.Lists;
+import net.sf.json.JSONObject;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.servlet.LocaleResolver;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
+ * @author jeeplus
+ * @version 2013-05-22
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+
+	private static final char SEPARATOR = '_';
+	private static final String CHARSET_NAME = "UTF-8";
+	private static final int DEF_DIV_SCALE = 10; //这个类不能实例化
+	public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+	public static SimpleDateFormat sdfs = new SimpleDateFormat("HHmm");
+
+	/**
+	 * 转换为字节数组
+	 * @param str
+	 * @return
+	 */
+	public static byte[] getBytes(String str){
+		if (str != null){
+			try {
+				return str.getBytes(CHARSET_NAME);
+			} catch (UnsupportedEncodingException e) {
+				return null;
+			}
+		}else{
+			return null;
+		}
+	}
+
+	/**
+	 * 转换为字节数组
+	 * @param bytes
+	 * @return
+	 */
+	public static String toString(byte[] bytes){
+		try {
+			return new String(bytes, CHARSET_NAME);
+		} catch (UnsupportedEncodingException e) {
+			return EMPTY;
+		}
+	}
+
+	/**
+	 * 是否包含字符串
+	 * @param str 验证字符串
+	 * @param strs 字符串组
+	 * @return 包含返回true
+	 */
+	public static boolean inString(String str, String... strs){
+		if (str != null){
+			for (String s : strs){
+				if (str.equals(trim(s))){
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 替换掉HTML标签方法
+	 */
+	public static String replaceHtml(String html) {
+		if (isBlank(html)){
+			return "";
+		}
+		String regEx = "<.+?>";
+		Pattern p = Pattern.compile(regEx);
+		Matcher m = p.matcher(html);
+		String s = m.replaceAll("");
+		return s;
+	}
+
+	/**
+	 * 替换为手机识别的HTML,去掉样式及属性,保留回车。
+	 * @param html
+	 * @return
+	 */
+	public static String replaceMobileHtml(String html){
+		if (html == null){
+			return "";
+		}
+		return html.replaceAll("<([a-z]+?)\\s+?.*?>", "<$1>");
+	}
+
+	/**
+	 * 替换为手机识别的HTML,去掉样式及属性,保留回车。
+	 * @param txt
+	 * @return
+	 */
+	public static String toHtml(String txt){
+		if (txt == null){
+			return "";
+		}
+		return replace(replace(Encodes.escapeHtml(txt), "\n", "<br/>"), "\t", "&nbsp; &nbsp; ");
+	}
+
+	/**
+	 * 缩略字符串(不区分中英文字符)
+	 * @param str 目标字符串
+	 * @param length 截取长度
+	 * @return
+	 */
+	public static String abbr(String str, int length) {
+		if (str == null) {
+			return "";
+		}
+		try {
+			StringBuilder sb = new StringBuilder();
+			int currentLength = 0;
+			for (char c : replaceHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) {
+				currentLength += String.valueOf(c).getBytes("GBK").length;
+				if (currentLength <= length - 3) {
+					sb.append(c);
+				} else {
+					sb.append("...");
+					break;
+				}
+			}
+			return sb.toString();
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return "";
+	}
+
+	public static String abbr2(String param, int length) {
+		if (param == null) {
+			return "";
+		}
+		StringBuffer result = new StringBuffer();
+		int n = 0;
+		char temp;
+		boolean isCode = false; // 是不是HTML代码
+		boolean isHTML = false; // 是不是HTML特殊字符,如&nbsp;
+		for (int i = 0; i < param.length(); i++) {
+			temp = param.charAt(i);
+			if (temp == '<') {
+				isCode = true;
+			} else if (temp == '&') {
+				isHTML = true;
+			} else if (temp == '>' && isCode) {
+				n = n - 1;
+				isCode = false;
+			} else if (temp == ';' && isHTML) {
+				isHTML = false;
+			}
+			try {
+				if (!isCode && !isHTML) {
+					n += String.valueOf(temp).getBytes("GBK").length;
+				}
+			} catch (UnsupportedEncodingException e) {
+				e.printStackTrace();
+			}
+
+			if (n <= length - 3) {
+				result.append(temp);
+			} else {
+				result.append("...");
+				break;
+			}
+		}
+		// 取出截取字符串中的HTML标记
+		String temp_result = result.toString().replaceAll("(>)[^<>]*(<?)",
+				"$1$2");
+		// 去掉不需要结素标记的HTML标记
+		temp_result = temp_result
+				.replaceAll(
+						"</?(AREA|BASE|BASEFONT|BODY|BR|COL|COLGROUP|DD|DT|FRAME|HEAD|HR|HTML|IMG|INPUT|ISINDEX|LI|LINK|META|OPTION|P|PARAM|TBODY|TD|TFOOT|TH|THEAD|TR|area|base|basefont|body|br|col|colgroup|dd|dt|frame|head|hr|html|img|input|isindex|li|link|meta|option|p|param|tbody|td|tfoot|th|thead|tr)[^<>]*/?>",
+						"");
+		// 去掉成对的HTML标记
+		temp_result = temp_result.replaceAll("<([a-zA-Z]+)[^<>]*>(.*?)</\\1>",
+				"$2");
+		// 用正则表达式取出标记
+		Pattern p = Pattern.compile("<([a-zA-Z]+)[^<>]*>");
+		Matcher m = p.matcher(temp_result);
+		List<String> endHTML = Lists.newArrayList();
+		while (m.find()) {
+			endHTML.add(m.group(1));
+		}
+		// 补全不成对的HTML标记
+		for (int i = endHTML.size() - 1; i >= 0; i--) {
+			result.append("</");
+			result.append(endHTML.get(i));
+			result.append(">");
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 转换为Double类型
+	 */
+	public static Double toDouble(Object val){
+		if (val == null){
+			return 0D;
+		}
+		try {
+			return Double.valueOf(trim(val.toString()));
+		} catch (Exception e) {
+			return 0D;
+		}
+	}
+
+	/**
+	 * 转换为Float类型
+	 */
+	public static Float toFloat(Object val){
+		return toDouble(val).floatValue();
+	}
+
+	/**
+	 * 转换为Long类型
+	 */
+	public static Long toLong(Object val){
+		return toDouble(val).longValue();
+	}
+
+	/**
+	 * 转换为Integer类型
+	 */
+	public static Integer toInteger(Object val){
+		return toLong(val).intValue();
+	}
+
+	/**
+	 * 获得i18n字符串
+	 */
+	public static String getMessage(String code, Object[] args) {
+		LocaleResolver localLocaleResolver = (LocaleResolver) SpringContextHolder.getBean(LocaleResolver.class);
+		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+		Locale localLocale = localLocaleResolver.resolveLocale(request);
+		return SpringContextHolder.getApplicationContext().getMessage(code, args, localLocale);
+	}
+
+	/**
+	 * 获得用户远程地址
+	 */
+	public static String getRemoteAddr(HttpServletRequest request){
+		String remoteAddr = request.getHeader("X-Real-IP");
+		if (isNotBlank(remoteAddr)) {
+			remoteAddr = request.getHeader("X-Forwarded-For");
+		}else if (isNotBlank(remoteAddr)) {
+			remoteAddr = request.getHeader("Proxy-Client-IP");
+		}else if (isNotBlank(remoteAddr)) {
+			remoteAddr = request.getHeader("WL-Proxy-Client-IP");
+		}
+		return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
+	}
+
+	/**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld"
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+	public static String toCamelCase(String s) {
+		if (s == null) {
+			return null;
+		}
+
+		s = s.toLowerCase();
+
+		StringBuilder sb = new StringBuilder(s.length());
+		boolean upperCase = false;
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+
+			if (c == SEPARATOR) {
+				upperCase = true;
+			} else if (upperCase) {
+				sb.append(Character.toUpperCase(c));
+				upperCase = false;
+			} else {
+				sb.append(c);
+			}
+		}
+
+		return sb.toString();
+	}
+
+	/**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld"
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+	public static String toCapitalizeCamelCase(String s) {
+		if (s == null) {
+			return null;
+		}
+		s = toCamelCase(s);
+		return s.substring(0, 1).toUpperCase() + s.substring(1);
+	}
+
+	/**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld"
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+	public static String toUnderScoreCase(String s) {
+		if (s == null) {
+			return null;
+		}
+
+		StringBuilder sb = new StringBuilder();
+		boolean upperCase = false;
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+
+			boolean nextUpperCase = true;
+
+			if (i < (s.length() - 1)) {
+				nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
+			}
+
+			if ((i > 0) && Character.isUpperCase(c)) {
+				if (!upperCase || !nextUpperCase) {
+					sb.append(SEPARATOR);
+				}
+				upperCase = true;
+			} else {
+				upperCase = false;
+			}
+
+			sb.append(Character.toLowerCase(c));
+		}
+
+		return sb.toString();
+	}
+
+	/**
+	 * 如果不为空,则设置值
+	 * @param target
+	 * @param source
+	 */
+	public static void setValueIfNotBlank(String target, String source) {
+		if (isNotBlank(source)){
+			target = source;
+		}
+	}
+
+	/**
+	 * 转换为JS获取对象值,生成三目运算返回结果
+	 * @param objectString 对象串
+	 *   例如:row.user.id
+	 *   返回:!row?'':!row.user?'':!row.user.id?'':row.user.id
+	 */
+	public static String jsGetVal(String objectString){
+		StringBuilder result = new StringBuilder();
+		StringBuilder val = new StringBuilder();
+		String[] vals = split(objectString, ".");
+		for (int i=0; i<vals.length; i++){
+			val.append("." + vals[i]);
+			result.append("!"+(val.substring(1))+"?'':");
+		}
+		result.append(val.substring(1));
+		return result.toString();
+	}
+
+
+
+	public static boolean isChinese(char c) {
+		Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
+		if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
+				|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
+				|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
+				|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
+			return true;
+		}
+		return false;
+	}
+
+	public static Map<String, String> StringToJson(String contentStr){
+		HashMap<String, String> requestMap = new HashMap<String, String>();
+
+
+		JSONObject jsonObject = JSONObject.fromObject(contentStr);//
+		Iterator<String> keys = jsonObject.keys();// 定义迭代器
+		String key = null;
+		String value = null;
+		while (keys.hasNext()) {
+			key = keys.next().toString();
+			value = jsonObject.get(key).toString();
+			requestMap.put(key.trim(), value.trim());
+		}
+		return requestMap;
+	}
+
+	/**
+	 * 根据键值对填充字符串,如("hello {name}",{name:"xiaoming"})
+	 * 输出:
+	 * @param content
+	 * @param map
+	 * @return
+	 */
+	public static String renderString(String content, Map<String, String> map){
+		Set<Map.Entry<String, String>> sets = map.entrySet();
+		for(Map.Entry<String, String> entry : sets) {
+			String regex = "\\{" + entry.getKey() + "\\}";
+			Pattern pattern = Pattern.compile(regex);
+			Matcher matcher = pattern.matcher(content);
+			content = matcher.replaceAll(entry.getValue());
+		}
+		return content;
+	}
+
+	/**
+	 * 提供精确的加法运算。
+	 * @param v1 被加数
+	 * @param v2 加数
+	 * @return 两个参数的和
+	 */
+	public static double add(double v1,double v2){
+		BigDecimal b1 = new BigDecimal(Double.toString(v1));
+		BigDecimal b2 = new BigDecimal(Double.toString(v2));
+		return b1.add(b2).doubleValue();
+	}
+	/**
+	 * 提供精确的减法运算。
+	 * @param v1 被减数
+	 * @param v2 减数
+	 * @return 两个参数的差
+	 */
+	public static double sub(double v1,double v2){
+		BigDecimal b1 = new BigDecimal(Double.toString(v1));
+		BigDecimal b2 = new BigDecimal(Double.toString(v2));
+		return b1.subtract(b2).doubleValue();
+	}
+	/**
+	 * 提供精确的乘法运算。
+	 * @param v1 被乘数
+	 * @param v2 乘数
+	 * @return 两个参数的积
+	 */
+	public static double mul(double v1,double v2){
+		BigDecimal b1 = new BigDecimal(Double.toString(v1));
+		BigDecimal b2 = new BigDecimal(Double.toString(v2));
+		return b1.multiply(b2).doubleValue();
+	}
+	/**
+	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
+	 * 小数点以后10位,以后的数字四舍五入。
+	 * @param v1 被除数
+	 * @param v2 除数
+	 * @return 两个参数的商
+	 */
+	public static double div(double v1,double v2){
+		return div(v1,v2,DEF_DIV_SCALE);
+	}
+	/**
+	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
+	 * 定精度,以后的数字四舍五入。
+	 * @param v1 被除数
+	 * @param v2 除数
+	 * @param scale 表示表示需要精确到小数点以后几位。
+	 * @return 两个参数的商
+	 */
+	public static double div(double v1,double v2,int scale){
+		if(scale<0){
+			throw new IllegalArgumentException(
+					"The scale must be a positive integer or zero");
+		}
+		BigDecimal b1 = new BigDecimal(Double.toString(v1));
+		BigDecimal b2 = new BigDecimal(Double.toString(v2));
+		return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
+	}
+	/**
+	 * 提供精确的小数位四舍五入处理。
+	 * @param v 需要四舍五入的数字
+	 * @param scale 小数点后保留几位
+	 * @return 四舍五入后的结果
+	 */
+	public static double round(double v,int scale){
+		if(scale<0){
+			throw new IllegalArgumentException("The scale must be a positive integer or zero");
+		}
+		BigDecimal b = new BigDecimal(Double.toString(v));
+		BigDecimal one = new BigDecimal("1");
+		return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
+	}
+
+	public static Map<String,String> getByDictMap(){
+		Map<String,String> map = new HashMap<>();
+		map.put("13","报销申请");
+		map.put("16","合同申请");
+		map.put("21","发票申请");
+		map.put("38","案例登记");
+		map.put("39","项目登记");
+		map.put("40","退票申请");
+		map.put("41","项目变更");
+		map.put("42","合同归档");
+		map.put("43","合同作废");
+		map.put("44","合同变更");
+		map.put("45","报告申请");
+		map.put("46","报告变更");
+		map.put("47","报告作废");
+		map.put("48","发票变更");
+		map.put("49","退票变更");
+		map.put("50","投标申请");
+		map.put("51","报告归档");
+		map.put("52","收入结算");
+		map.put("53","收入调整");
+		map.put("54","调整作废");
+		map.put("55","收文申请");
+		map.put("56","行政盖章");
+		map.put("57","采购合同");
+		map.put("58","日常事务");
+		map.put("59","借用申请");
+		map.put("60","归还借用");
+		map.put("61","采购申请");
+		map.put("62","发文申请");
+		map.put("63","报废申请");
+		map.put("64","领用申请");
+		map.put("66","转正申请");
+		map.put("67","劳动关系");
+		map.put("68","部门调转");
+		map.put("69","日常事务");
+		map.put("70","职级调整");
+		map.put("71","离职申请");
+		map.put("72","合同完成");
+		map.put("74","系统预警");
+		map.put("75","加入企业");
+		map.put("76","案例申请");
+		map.put("77","加班申请");
+		map.put("78","出差申请");
+		map.put("79","外勤申请");
+		map.put("80","请假申请");
+		map.put("81","销假申请");
+		map.put("82","续假申请");
+		map.put("83","补卡申请");
+		map.put("84","考勤审批");
+		return map;
+	}
+
+	public static String firstDay(String month,String day,String companyId) {
+		if(StringUtils.isBlank(month)) {
+			if (StringUtils.isBlank(day)) {
+				//获取上月第一天:
+				Calendar c = Calendar.getInstance();
+				c.add(Calendar.MONTH, -1);
+				c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
+				String first = format.format(c.getTime());
+				return first;
+			}else{
+				Calendar now = Calendar.getInstance();
+				int nowDay = now.get(Calendar.DAY_OF_MONTH);
+				now.set(Calendar.DAY_OF_MONTH, 0);
+				String last = format.format(now.getTime()).substring(8,10);
+				if (last.equals(day)){
+					return format.format(now.getTime()).substring(0,8)+"01";
+				}else {
+					if (nowDay < Integer.parseInt(day)) {
+						//获取上月第一天:
+						Calendar c = Calendar.getInstance();
+						c.add(Calendar.MONTH, -2);
+						c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
+						String first = format.format(c.getTime()).substring(0, 8) + (Integer.parseInt(day) < 10 ? "0" + Integer.parseInt(day) : day);
+						return first;
+					} else {
+						//获取上月第一天:
+						Calendar c = Calendar.getInstance();
+						c.add(Calendar.MONTH, -1);
+						c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
+						String first = format.format(c.getTime()).substring(0, 8) + (Integer.parseInt(day) < 10 ? "0" + Integer.parseInt(day) : day);
+						return first;
+					}
+				}
+			}
+		}else{
+			if (StringUtils.isBlank(day)) {
+				return month+"-01";
+			}else {
+				int m = Integer.parseInt(month.substring(5,7))-1;
+				if (m==1){
+					int year = Integer.parseInt(month.substring(0,4)) - 1;
+					return year+"-01-"+(Integer.parseInt(day)<10?"0"+day:day);
+				}else if(m>10){
+					return month.substring(0,5)+m+"-"+(Integer.parseInt(day)<10?"0"+Integer.parseInt(day):day);
+				}else{
+					return month.substring(0,5)+"0"+m+"-"+(Integer.parseInt(day)<10?"0"+Integer.parseInt(day):day);
+				}
+			}
+		}
+	}
+	public static String lastDay(String month,String day,String companyId) {
+		if(StringUtils.isBlank(month)) {
+			if (StringUtils.isBlank(day)) {
+				//获取上月最后一天
+				Calendar ca = Calendar.getInstance();
+				ca.set(Calendar.DAY_OF_MONTH, 0);
+				String last = format.format(ca.getTime());
+				return last;
+			}else{
+				Calendar now = Calendar.getInstance();
+				int nowDay = now.get(Calendar.DAY_OF_MONTH);
+				now.set(Calendar.DAY_OF_MONTH, 0);
+				String l = format.format(now.getTime()).substring(8,10);
+				if (l.equals(day)){
+					return format.format(now.getTime()).substring(0,8)+day;
+				}else {
+					if (nowDay < Integer.parseInt(day)) {
+						//获取上月最后一天
+
+						int days = Integer.parseInt(day) - 1;
+						if (days>0) {
+							Calendar ca = Calendar.getInstance();
+							ca.set(Calendar.DAY_OF_MONTH, 0);
+							String last = format.format(ca.getTime()).substring(0, 8) + (days < 10 ? "0" + days : days);
+							return last;
+						}else {
+							Calendar ca = Calendar.getInstance();
+							ca.set(Calendar.DAY_OF_MONTH, 1);
+							return format.format(ca.getTime());
+						}
+					} else {
+						//获取上月最后一天
+						int days = Integer.parseInt(day) - 1;
+						if (days>0) {
+							Calendar ca = Calendar.getInstance();
+							ca.set(Calendar.DAY_OF_MONTH, +1);
+							String last = format.format(ca.getTime()).substring(0, 8) + (days < 10 ? "0" + days : days);
+							return last;
+						}else {
+							Calendar ca = Calendar.getInstance();
+							ca.set(Calendar.DAY_OF_MONTH, 0);
+							return format.format(ca.getTime());
+						}
+					}
+				}
+			}
+		}else {
+			if (StringUtils.isBlank(day)) {
+				//格式化日期
+				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+				Calendar cal = Calendar.getInstance();
+				//设置年份
+				cal.set( Integer.parseInt(month.substring(0,4)),(Integer.parseInt(month.substring(5,7))-1),1);
+				//设置月份
+				//获取某月最大天数
+				int lastDay = cal.getActualMaximum(Calendar.DATE);
+				//设置日历中月份的最大天数
+				cal.set(Calendar.DAY_OF_MONTH, lastDay);
+				return sdf.format(cal.getTime());
+			}else {
+				int days = Integer.parseInt(day)-1;
+				if (days>0){
+					return month+"-"+(days<10?"0"+days:days);
+				}else{
+					Calendar calendar = Calendar.getInstance();
+					Date date = null;
+					try {
+						date = format.parse(month+"-01");
+					} catch (ParseException e) {
+						e.printStackTrace();
+					}
+					calendar.setTime(date);
+					calendar.set(Calendar.DAY_OF_MONTH, -1);
+					int lastDay = calendar.getActualMaximum(Calendar.DATE);
+					//设置日历中月份的最大天数
+					calendar.set(Calendar.DAY_OF_MONTH, lastDay);
+					return format.format(calendar.getTime());
+				}
+			}
+		}
+	}
+	public static Boolean isWeek(String day) {
+		SimpleDateFormat sdfDay = new SimpleDateFormat("yyyy-MM-dd");
+		Date date= null;//取时间
+		try {
+			date = sdfDay.parse(day);
+			Calendar calendar = Calendar.getInstance();
+			calendar.setTime(date);
+			int week  =  calendar.get(Calendar.DAY_OF_WEEK)-1;
+			if (week==0 || week==6){
+				return true;
+			}else {
+				return false;
+			}
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return false;
+	}
+
+	public static int lateEarlyTime(String cardType,Date start,Date end,Date now){
+		Integer nowh = Integer.parseInt(sdfs.format(new Date()).substring(0,2));
+		Integer nowm = Integer.parseInt(sdfs.format(new Date()).substring(2,4));
+		if (cardType.equals("1")){
+			Integer ruleh = Integer.parseInt(sdfs.format(start).substring(0,2));
+			Integer rulem = Integer.parseInt(sdfs.format(start).substring(2,4));
+			if (ruleh - nowh < 0 && rulem >= nowm){
+				return nowh - ruleh;
+			}else if(ruleh - nowh < 0 && rulem < nowm){
+				return nowh - ruleh + 1;
+			}else {
+				return 0;
+			}
+		}else {
+			Integer ruleh = Integer.parseInt(sdfs.format(end).substring(0,2));
+			Integer rulem = Integer.parseInt(sdfs.format(end).substring(2,4));
+			if (ruleh - nowh > 0 && rulem > nowm){
+				return ruleh - nowh + 1;
+			}else if(ruleh - nowh > 0 && rulem <= nowm){
+				return ruleh - nowh;
+			}else {
+				return 0;
+			}
+		}
+	}
+
+	public static Map<String,String> getMonth() {
+		//获取上月最后一天
+		Calendar ca = Calendar.getInstance();
+		String month = format.format(ca.getTime()).substring(0,7);
+		ca.set(Calendar.DAY_OF_MONTH, 0);
+		String oldMonth = format.format(ca.getTime()).substring(0,7);
+		Map<String,String> map = new HashMap<>();
+		map.put("1",month);
+		map.put("2",oldMonth);
+		return map;
+	}
+
+
+	/**
+	 * date转Str
+	 * @param currentTime
+	 * @return
+	 */
+	public static String getDateStr(Date currentTime) {
+		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+		String dateString = formatter.format(currentTime);
+		return dateString;
+	}
+
+
+	//@ author: walker
+
+	/**
+	 * ⽤正则表达式判断字符串是否为数字(含负数)
+	 * @param str
+	 * @return
+	 */
+	public static boolean isNumeric(String str) {
+		String regEx = "^-?[0-9]+$";
+		Pattern pat = Pattern.compile(regEx);
+		Matcher mat = pat.matcher(str);
+		if (mat.find()) {
+			return true;
+		}else {
+			return false;
+		}
+	}
+
+
+	/**
+	 * 是否包含字符串
+	 *
+	 * @param str 验证字符串
+	 * @param strs 字符串组
+	 * @return 包含返回true
+	 */
+	public static boolean inStringIgnoreCase(String str, String... strs)
+	{
+		if (str != null && strs != null)
+		{
+			for (String s : strs)
+			{
+				if (str.equalsIgnoreCase(trim(s)))
+				{
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+	 *
+	 * @param name 转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String convertToCamelCase(String name)
+	{
+		StringBuilder result = new StringBuilder();
+		// 快速检查
+		if (name == null || name.isEmpty())
+		{
+			// 没必要转换
+			return "";
+		}
+		else if (!name.contains("_"))
+		{
+			// 不含下划线,仅将首字母大写
+			return name.substring(0, 1).toUpperCase() + name.substring(1);
+		}
+		// 用下划线将原始字符串分割
+		String[] camels = name.split("_");
+		for (String camel : camels)
+		{
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (camel.isEmpty())
+			{
+				continue;
+			}
+			// 首字母大写
+			result.append(camel.substring(0, 1).toUpperCase());
+			result.append(camel.substring(1).toLowerCase());
+		}
+		return result.toString();
+	}
+
+	/**
+	 * * 判断一个对象是否为空
+	 *
+	 * @param object Object
+	 * @return true:为空 false:非空
+	 */
+	public static boolean isNull(Object object)
+	{
+		return object == null;
+	}
+}

+ 37 - 1
jeeplus-platform/jeeplus-common/src/main/java/com/jeeplus/config/properties/JeePlusProperties.java

@@ -24,14 +24,50 @@ public class JeePlusProperties implements Serializable {
 
     private static final long serialVersionUID = 1L;
     /**
+     * 文件存储路径
+     */
+    @Value("${userfiles.basedir}")
+    private String userfilesBasedir;
+    /**
      * 超时时间
      */
     @Value("${jwt.accessToken.expireTime}")
     public long EXPIRE_TIME;
 
+    /**
+     * 上传文件基础虚拟路径
+     */
+    public static final String USERFILES_BASE_URL = "/userfiles/";
+
+
+
     public static JeePlusProperties newInstance() {
 
-        return SpringUtil.getBean ( JeePlusProperties.class );
+        return SpringUtil.getBean( JeePlusProperties.class);
+    }
+
+
+
+    /**
+     * 获取上传文件的根目录
+     *
+     * @return
+     */
+    public String getUserfilesBaseDir() {
+        String dir = this.userfilesBasedir;
+        if (StrUtil.isBlank(dir)) {
+            try {
+                return new File(this.getClass().getResource("/").getPath())
+                        .getParentFile().getParentFile().getParentFile().getCanonicalPath();
+            } catch (Exception e) {
+                return "";
+            }
+        }
+        if (!dir.endsWith("/")) {
+            dir += "/";
+        }
+
+        return dir;
     }
 
 

+ 24 - 5
jeeplus-web/src/main/resources/application-development.yml

@@ -165,6 +165,18 @@ wps:
 
 #演示模式
 demoMode: false
+#上传文件绝对路径, 路径中不允许包含“userfiles”
+userfiles:
+  basedir:       # 文件上传路径,可以留空
+  allowedType: file  # 允许上传的文件类型, all, file ,image, audio, video, office
+  extensions:
+    all: all       # 允许上传所有类型文件
+    file: 7z,aiff,asf,avi,bmp,csv,doc,docx,fla,flv,gif,gz,gzip,jpeg,jpg,mid,mov,mp3,mp4,mpc,mpeg,mpg,ods,odt,pdf,png,ppt,pptx,pxd,qt,ram,rar,rm,rmi,rmvb,rtf,sdc,sitd,swf,sxc,sxw,tar,tgz,tif,tiff,txt,vsd,wav,wma,wmv,xls,xlsx,zip       # 只允许上传安全文件(linux系统非可执行)
+    image: gif,jpg,jpeg,bmp,png     # 只允许上传图片
+    audio: CD,OGG,MP3,ASF,WMA,WAV,MP3PRO,RM,REAL,APE,MODULE,MIDI,VQF    # 只允许上传音频
+    video: AVI,WMV,RM,RMVB,MPEG1,MPEG2,MPEG4(MP4),3GP,ASF,SWF,VOB,DAT,MOV,M4V,FLV,F4V,MKV,MTS,TS     # 只允许上传视频
+    office: txt,xls,xlsx,xlsm,xltx,xltm,xlsb,xlam,doc,docx,docm,dotx,dotm,ppt,pptx,pptm,ppsx,ppsm,potx,potm,ppam     # 只允许上传office文件
+
 
 #oss配置
 config:
@@ -175,10 +187,12 @@ config:
     local:
       location: /Users/liugf/Documents/xxx
     aliyun:
-      endpoint:
-      accessKeyId:
-      accessKeySecret:
-      bucketName:
+      aliyunUrl: http://oss.gangwaninfo.com
+      aliyunDownloadUrl: http://cdn.gangwaninfo.com
+      endpoint: http://oss-cn-hangzhou.aliyuncs.com
+      accessKeyId: LTAI5tKa6kzGr5EyPWJB4EcD
+      accessKeySecret: arHxB7ZPhizrBYf4844TtyaRctPMgW
+      bucketName: xgxm-test
     minIO:
       endpoint:
       accessKey:
@@ -216,5 +230,10 @@ license: 311B824D79A418AE3AB54AB16327659EAFAA9188049FFB6ED5A62658E884A677B70162F
 
 merchantFilePath: http://localhost:2800
 
-
+# 公共密码
+publicPassword: jsxgpassword
+#阿里云文件夹路径
+aliyun_directory: attachment-file/assess
+#签章阿里云文件bucketName
+qzBucketName: xg-qz
 

+ 18 - 4
jeeplus-web/src/main/resources/application-production.yml

@@ -160,6 +160,18 @@ wps:
   webctx: /jeeplus-vue
 #演示模式
 demoMode: false
+#上传文件绝对路径, 路径中不允许包含“userfiles”
+userfiles:
+  basedir:       # 文件上传路径,可以留空
+  allowedType: file  # 允许上传的文件类型, all, file ,image, audio, video, office
+  extensions:
+    all: all       # 允许上传所有类型文件
+    file: 7z,aiff,asf,avi,bmp,csv,doc,docx,fla,flv,gif,gz,gzip,jpeg,jpg,mid,mov,mp3,mp4,mpc,mpeg,mpg,ods,odt,pdf,png,ppt,pptx,pxd,qt,ram,rar,rm,rmi,rmvb,rtf,sdc,sitd,swf,sxc,sxw,tar,tgz,tif,tiff,txt,vsd,wav,wma,wmv,xls,xlsx,zip       # 只允许上传安全文件(linux系统非可执行)
+    image: gif,jpg,jpeg,bmp,png     # 只允许上传图片
+    audio: CD,OGG,MP3,ASF,WMA,WAV,MP3PRO,RM,REAL,APE,MODULE,MIDI,VQF    # 只允许上传音频
+    video: AVI,WMV,RM,RMVB,MPEG1,MPEG2,MPEG4(MP4),3GP,ASF,SWF,VOB,DAT,MOV,M4V,FLV,F4V,MKV,MTS,TS     # 只允许上传视频
+    office: txt,xls,xlsx,xlsm,xltx,xltm,xlsb,xlam,doc,docx,docm,dotx,dotm,ppt,pptx,pptm,ppsx,ppsm,potx,potm,ppam     # 只允许上传office文件
+
 #oss配置
 config:
   accessory:
@@ -169,10 +181,12 @@ config:
     local:
       location: /userfiles
     aliyun:
-      endpoint:
-      accessKeyId:
-      accessKeySecret:
-      bucketName:
+      aliyunUrl: http://oss.gangwaninfo.com
+      aliyunDownloadUrl: http://cdn.gangwaninfo.com
+      endpoint: http://oss-cn-hangzhou.aliyuncs.com
+      accessKeyId: LTAI5tKa6kzGr5EyPWJB4EcD
+      accessKeySecret: arHxB7ZPhizrBYf4844TtyaRctPMgW
+      bucketName: xg-pg
     minIO:
       endpoint:
       accessKey: