1、common下模块 缺少.iml文件

或者

mvn idea:module

2、common下子模块未生成jar包

3、common下子工程,类需要spring管理

spring.factories中添加spring管理类的路径

4、富文本加密上传

问题:前台vue editor组件上传内容,图片地址会莫名丢失,有偶发性bug

解决:上传前,encode或encodeURIComponent编码,展示前 解码

//编码
this.form.particulars = encodeURI(this.form.particulars)
//解码
this.form.particulars = decodeURI(this.form.particulars)


encodeURI是对url中的查询字符串部分进行转义

encodeURIComponent对整个url进行转义,包括空格、英文冒号、斜杠等

至于decodeURI和decodeURIComponent,只要知道decodeURI和encodeURI是互逆操作,decodeURIComponent和encodeURIComponent是互逆操作

5、富文本上传,标签被xss过滤

nacos 网关 yml 中添加

6、system模块中集成websocket

pom.xml中添加

<!--        websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

package com.quan.system.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Created with IntelliJ IDEA.
 * @ Description:
 * @ ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@Component
@Slf4j
@Service
@ServerEndpoint(value = "/imserver/{sid}")
public class WebSocketServer {
    //当前在线连接数
    private static int onlineCount = 0;
    //存放每个客户端对应的MyWebSocket对象
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    private Session session;

    //接收sid
    private String sid = "";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        this.sid = sid;
        addOnlineCount();           //在线数加1
        try {
            sendMessage("conn_success");
            log.info("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1

        log.info("释放的sid为:"+sid);

        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());

    }

    /**
     * 收到客户端消息后调用的方法
     * @ Param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口" + sid + "的信息:" + message);
        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @ Param session
     * @ Param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        log.info("推送消息到窗口" + sid + ",推送内容:" + message);

        for (WebSocketServer item : webSocketSet) {
            try {
                //为null则全部推送
                if (sid == null) {
//                    item.sendMessage(message);
                } else if (item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }
}
package com.quan.system.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    /**
     * 注入一个ServerEndpointExporter,该bean会自动舌侧使用 @ServerEndpoint注解声明websocket endpoind
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

WebMvcConfig中增加 不拦截地址

 nocas网关中增加白名单

 前台websocket通信地址为

'ws://localhost:8080/system/imserver/'

7、vue中配置排序参数

后台默认集成了PageHelper,可在前端查询参数中配置 orderByColumn排序的列,isAsc排序方向即可

8、修改菜单栏宽度

9、main.js 判断开发环境,生成环境

10、新增数据返回自增主键

<insert id="insertNotice" parameterType="SysNotice" keyColumn="notice_id" keyProperty="noticeId" useGeneratedKeys="true">

11、command line is too long

 

12、[loadNacosData,104] - parse data from Nacos error

配置文件中有中文,而nacos读取配置文件时默认编码为utf-8,而通过cmd启动项目默认使用gbk。

java -jar -Dfile.encoding=utf-8 包名.jar

13、打包成运行jar,jar包和class文件分离

<build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <!-- 是否要把第三方jar加入到类构建路径 -->
                            <addClasspath>true</addClasspath>
                            <!-- 外部依赖jar包的最终位置 -->
                            <classpathPrefix>lib/</classpathPrefix>
                            <!--指定jar程序入口-->
                            <mainClass>com.quan.system.QuanSystemApplication</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <!--拷贝依赖到jar外面的lib目录-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 拷贝项目依赖包到lib/目录下 -->
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

14、自定义过滤器filter

gateway-dev.yml 中给需要配置过滤器的子模块增加配置,如下给system模块增加CacheRequestFilter 和 ApiFilter

 网关模块中增加ApiFilter过滤器

ApiFilter.java

package com.cazql.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cazql.common.core.utils.ServletUtils;
import com.cazql.common.core.utils.StringUtils;
import com.cazql.gateway.config.properties.CaptchaProperties;
import com.cazql.gateway.service.ValidateCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 外部接口过滤器
 *
 * @author ruoyi
 */
@Component
public class ApiFilter extends AbstractGatewayFilterFactory<Object>
{
    private final static String[] VALIDATE_URL = new String[] { "/system/api" };

    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            // 非VALIDATE_URL 中请求,不处理 
            if (!StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL))
            {
                return chain.filter(exchange);
            }

            try
            {
                String rspStr = resolveBodyFromRequest(request);
                JSONObject obj = JSON.parseObject(rspStr);
            }
            catch (Exception e)
            {
                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
            }
            return chain.filter(exchange);
        };
    }

    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest)
    {
        // 获取请求体
        Flux<DataBuffer> body = serverHttpRequest.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        return bodyRef.get();
    }
}

15、SM2加解密

package com.cazql.gateway.filter;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.cazql.common.core.utils.uuid.UUID;

import java.io.*;
import java.security.KeyPair;

public class t {
    public static void main(String[] args) throws Exception {
        System.out.println(UUID.randomUUID().toString().replace("-","").length());

        String text = "我是一段测试aaaa";

        KeyPair pair = SecureUtil.generateKeyPair("SM2");

        byte[] privateKey = pair.getPrivate().getEncoded();
        byte[] publicKey = pair.getPublic().getEncoded();

        System.out.println(HexUtil.encodeHexStr(privateKey));
        System.out.println(HexUtil.encodeHexStr(publicKey));


        outPk(privateKey,"aaa.privateKey");
        outPk(publicKey,"aaa.publicKey");

        privateKey = reakPk("aaa.privateKey");
        publicKey = reakPk("aaa.publicKey");

        //公钥加密
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
        // 私钥解密
        sm2 = SmUtil.sm2(privateKey, null);
        String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));

        System.out.println(decryptStr);
    }

    public static void outPk(byte[] pk,String fileName) throws Exception {
        File file = new File("C:\\Users\\Lenovo\\Desktop\\"+fileName);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(pk);
        fos.flush();
        fos.close();
    }

    public static byte[] reakPk(String fileName) throws Exception{

        File file = new File("C:\\Users\\Lenovo\\Desktop\\"+fileName);
        int _bufferSize = (int) file.length();
        //定义buffer缓冲区大小
        byte[] buffer = new byte[_bufferSize];
        FileInputStream fos = new FileInputStream(file);
        fos.read(buffer);
        fos.close();
        return buffer;
    }
}

16、SM4加解密

        //随机生成sm4加密key
        String sm4Key = "GJwsXX_BzW=gJWJW";
        System.out.println("sm4Key:"+sm4Key);

        //sm4加密业务报文
        SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding,sm4Key.getBytes());
        String sm4Encrypt = sm4.encryptBase64("12345");
        System.out.println("sm4加密报文:"+sm4Encrypt);
        String sm4Decrypt = sm4.decryptStr(sm4Encrypt);
        System.out.println("sm4解密报文:"+sm4Decrypt);
        System.out.println("=============================");

17、vue 前台sm4加解密

npm install gm-crypt

utils下建sm4.js

const SM4 = require('gm-crypt').sm4

let sm4Config = {
    key: 'GJwsXX_BzW=gJWJW',
    mode: 'ecb', // 加密的方式有两种,ecb和cbc两种,也是看后端如何定义的,不过要是cbc的话下面还要加一个iv的参数,ecb不用
    // optional: this is the cipher data's type; Can be 'base64' or 'text'
    cipherType: 'base64' // default is base64
}
let sm4 = new SM4(sm4Config)

export default sm4

main.js

import SM4 from '@/utils/sm4'

Vue.prototype.SM4 = SM4

vue页面中使用

        let Account = this.SM4.encrypt("12345"); //账号加密
        let Account1 = this.SM4.decrypt(Account); //账号解密

18、nginx  Request entity too large

client_max_body_size 100m;

19、富文本增加功能按钮提示

新建quill-title.js

const titleConfig = {
    'ql-bold': '加粗',
    'ql-color': '颜色',
    'ql-font': '字体',
    'ql-code': '插入代码',
    'ql-italic': '斜体',
    'ql-link': '添加链接',
    'ql-background': '背景颜色',
    'ql-size': '字体大小',
    'ql-strike': '删除线',
    'ql-script': '上标/下标',
    'ql-underline': '下划线',
    'ql-blockquote': '引用',
    'ql-header': '标题',
    'ql-indent': '缩进',
    'ql-list': '列表',
    'ql-align': '文本对齐',
    'ql-direction': '文本方向',
    'ql-code-block': '代码块',
    'ql-formula': '公式',
    'ql-image': '图片',
    'ql-video': '视频',
    'ql-clean': '清除字体样式'
  }
  export function addQuillTitle () {
    const oToolBar = document.querySelector('.ql-toolbar'),
      aButton = oToolBar.querySelectorAll('button'),
      aSelect = oToolBar.querySelectorAll('select')
    aButton.forEach(function (item) {
      if (item.className === 'ql-script') {
        item.value === 'sub' ? item.title = '下标' : item.title = '上标'
      } else if (item.className === 'ql-indent') {
        item.value === '+1' ? item.title = '向右缩进' : item.title = '向左缩进'
      } else {
        item.title = titleConfig[item.classList[0]]
      }
    })
    aSelect.forEach(function (item) {
      item.parentNode.title = titleConfig[item.classList[0]]
    })
  }
  

页面引入

import { addQuillTitle } from './quill-title.js'

mounted() {
    addQuillTitle();
  },

20、取消分页合理化

21、高并发下防止余额为负

1.查询时加悲观锁

 2.service方法中增加事务注解

 3.controller业务处理层增加事务注解

业务处理层最终执行完 update 后 锁解除,否则排序等待锁解除,这样就不会发生超扣

利用的是行级锁,上锁->更新->解锁

22、nginx配置ssl

阿里云下载nginx证书

上传到cert下

 server配置

server {
    listen       443 default_server  ssl;
    server_name  www.域名;

    ssl_certificate      cert/9057355_域名.pem; #存放.pem证书的路径

    ssl_certificate_key  cert/9057355_域名.key; #存放.key证书的路径

    server_tokens        off;
    
    ......
}

重启nginx即可

23、jar包启动配置ssl

下载tomcat类型证书

pfx证书放到resources下

 

重启jar即可

24、仅本人数据权限配置

 @DataScope(userAlias = "u")

 

 <!-- 数据范围过滤 -->
            ${params.dataScope}

25、本地jar包 打包后没在MANIFEST.MF中导致启动报错类找不到

本地jar包放到 src下

<!-- 引入本地lib包 -->
        <!-- 天翼云对象存储oss start -->
        <dependency>
            <groupId>cn.ctyun</groupId>
            <artifactId>oos-sdk</artifactId>
            <scope>system</scope>
            <version>6.5.0</version>
            <systemPath>${project.basedir}/src/lib/oos-sdk-6.5.0.jar</systemPath>
        </dependency>

添加本地jar包

                        <manifestEntries>
                            <Class-Path>.</Class-Path>
                            <Class-Path>lib/oos-sdk-6.5.0.jar</Class-Path>
                        </manifestEntries>

26、支持多字段排序

PageDomain.java 中增加以下代码

        //支持多字段排序 start
        String orderByStr = "";
        String[] columns = orderByColumn.split(",");
        String[] isAscs = isAsc.split(",");
        for (int i = 0; i < columns.length; i++) {
            String column = columns[i];
            String order = isAscs[i];
            if("ascending".equals(order)){//多字段,不被格式化,特殊处理
                order = "asc";
            }
            if("descending".equals(order)){
                order = "desc";
            }
            orderByStr += "," + StringUtils.toUnderScoreCase(column) + " " + order;
        }
        orderByStr = orderByStr.substring(1, orderByStr.length());

        //支持多字段排序 end
        return orderByStr;

请求参数如下:

27、增加滑块验证码

安装插件

npm install --save vue-monoplasty-slide-verify

main.js中

//滑块验证码
import SlideVerify from 'vue-monoplasty-slide-verify';
Vue.use(SlideVerify);

//全局组件挂载
Vue.component('SliderCode',SliderCode)
//滑块验证码
import SliderCode from '@/components/SliderCode'

添加子组件SliderCode

<!--滑块验证码 
使用说明:
1.父组件调用open方法打开滑块验证码
2.验证通过后调用父组件 verifySuccess 方法
示例:<slider-code ref="sliderCodeRef" @verifySuccess="verifySuccess" />
-->
<template>
  <div>
    <el-dialog title="滑块验证码" :visible.sync="VerifyShow" width="370px" :close-on-click-modal="false">
      <div class="page-slidecode">
        <!--    l:滑块的边长
               r:滑块突出圆的半径
               w:canvas画布的宽
               h:canvas画布的高
               imgs:背景图数组(放到public目录下)
               sliderText:滑块底纹文字
               accuracy:滑动验证的误差范围,默认值为5
               show:是否显示刷新按钮

               success:验证码匹配成功的回调
               fail:验证码未匹配的回调
               refresh:点击刷新按钮后的回调函数
        fulfilled:刷新成功之后的回调函数-->
        <slide-verify
          ref="slideblock"
          :l="42"
          :r="10"
          :w="330"
          :h="165"
          :imgs="imgs"
          :accuracy="accuracy"
          :slider-text="text"
          @again="onAgain"
          @fulfilled="onFulfilled"
          @success="onSuccess"
          @fail="onFail"
          @refresh="onRefresh"
        />
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 滑块验证
      VerifyShow: false,
      accuracy: 5,
      imgs: ["/src/001.png"],
      text: "向右滑动~"
    };
  },
  methods: {
    open() {
      this.VerifyShow = true;
      this.onAgain();
    },
    onFail() {
      this.$message({
        message: "验证未通过",
        type: "error"
      });
    },
    onSuccess() {
      console.log("验证通过");
      this.VerifyShow = false;
      this.$emit("verifySuccess");
    },
    onRefresh() {
      // console.log('点击了刷新小图标');
    },
    onFulfilled() {
      // console.log('刷新成功啦!');
    },
    onAgain() {
      // console.log('检测到非人为操作的哦!');
      // 刷新
      this.$refs.slideblock.reset();
    }
  }
};
</script>

<style scoped>
.el-dialog__header {
  padding: unset;
}
</style>


使用

<slider-code ref="sliderCodeRef" @verifySuccess="verifySuccess" />



//打开滑块验证码的校验
this.$refs.sliderCodeRef.open();


//验证通过执行业务逻辑
    verifySuccess() {
      alert("验证通过");
    },

27、禁用Actuator端点(Actuator未授权访问漏洞)

yml中增加

# 禁用Actuator端点
management:
  server:
    port: -1

28、禁用swagger

29、调用存储过程传参 报错

### Error querying database.  Cause: java.lang.ClassCastException: net.sf.jsqlparser.statement.execute.Execute cannot be cast to net.sf.jsqlparser.statement.select.Select
### Cause: java.lang.ClassCastException: net.sf.jsqlparser.statement.execute.Execute cannot be cast to net.sf.jsqlparser.statement.select.Select

原来的写法

    <select id="listNew" parameterType="java.util.Map" >
        CALL update_dy_stock_new(
        #{receiveSite},
        #{coalName},
        #{planCalorificValue},
        #{planSulfurContent},
        #{isSpecialUnitAccounting},
        #{specialAccountingId},
        #{financeAuditStatus}
        )
    </select>

改后的写法

    <update id="listNew" parameterType="java.util.Map" >
        CALL update_dy_stock_new(
        #{receiveSite},
        #{coalName},
        #{planCalorificValue},
        #{planSulfurContent},
        #{isSpecialUnitAccounting},
        #{specialAccountingId},
        #{financeAuditStatus}
        )
    </update>

原因:用select 标签,会有一步count统计,导致出错,换其他标签即可

30、vue 遍历移除list中item

    //删除请求参数
    delParams(item) {
      let targetIndex = this.form.requestParams.findIndex(
        itemTemp => itemTemp === item
      );
      if (targetIndex !== -1) {
        this.form.requestParams.splice(targetIndex, 1);
      }
    },

31、swagger配置

请求地址:http://localhost:8080/swagger-ui/index.html

SwaggerConfig.java 中配置

配置请求头header

.globalRequestParameters(Collections.singletonList(new RequestParameterBuilder()
                                .name("authorizationTags")
                                .description(" 授权标识 ")
                                .in(ParameterType.HEADER)
                                .required(true)
                                .build()))

    @ApiOperation("获取共享数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "databaseName", value = "库名称", required = true, dataType = "string", paramType = "path"),
            @ApiImplicitParam(name = "tableName", value = "表名称", required = true, dataType = "string", paramType = "path"),
            @ApiImplicitParam(name = "authorizationTags", value = "授权标识", required = true, dataType = "string", paramType = "header"),
    })
    @PostMapping("/share/{databaseName}/{tableName}")
    public AjaxResult share(@RequestHeader("authorizationTags") String authorizationTags, @RequestBody String body, @PathVariable("databaseName") String databaseName,
                            @PathVariable("tableName") String tableName) {

32、mybatis返回map 保留空值

<setting name="callSettersOnNulls" value="true"/>

33、根据模板导出word文件

pom.xml

        <!-- 根据模板导出excel/word -->
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.6.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>

controller

    @GetMapping("/downloadTemplate/{id}")
    @Operation(summary = "下载模板")
    public void downloadTemplate(HttpServletResponse response,@PathVariable("id") Long id) throws IOException {
        PersonTemplateRespVO personTemplate = personTemplateService.getPersonTemplate(templateId);
        String templateType = personTemplate.getTemplateType();
        /*文本*/
        Map<String, Object> map = new HashMap<>();
        map.put("name", "Sayi");
        map.put("IDNumber", "612255");
        // url资源转化为file流
        File file = FileUtils.urlToFile(personTemplate.getFile());
        // 获取文件绝对路径
        String absolutePath = file.getAbsolutePath();
        ServletOutputStream outputStream = response.getOutputStream();

        if("Word".equals(templateType)){
            // 模板注意 用{{}} 来表示你要用的变量
            XWPFTemplate template = XWPFTemplate.compile(absolutePath).render(map);
            file.delete();
            template.write(outputStream);
            outputStream.close();
            template.close();
        }else if("Excel".equals(templateType)){
            // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
            String fileName = absolutePath + "simpleFill" + System.currentTimeMillis() + ".xlsx";
            // 这里 会填充到第一个sheet, 然后文件流会自动关闭
            EasyExcel.write(fileName).withTemplate(absolutePath).sheet().doFill(map);
            file.delete();
            //以流的形式写出
            //设置响应头
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", 999 + ".xlsx"));
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", -1);
            response.setCharacterEncoding("UTF-8");
            //将文件流写入响应流
            FileInputStream fis = new FileInputStream(fileName);
            IOUtils.copy(fis, outputStream);
            //关闭文件流和响应输出流
            fis.close();
            FileUtils.deleteFileByPath(fileName);
            outputStream.flush();
            outputStream.close();
        }
    }

urlToFile

/**
     * url资源转化为file流
     * @param url2
     * @return
     */
    public static File urlToFile(String url2) throws MalformedURLException {
        URL url = new URL(url2);
        InputStream is = null;
        File file = null;
        FileOutputStream fos = null;
        try {
            file = File.createTempFile("tmp", url2.substring(url2.lastIndexOf(".")));
            URLConnection urlConn = null;
            urlConn = url.openConnection();
            is = urlConn.getInputStream();
            fos = new FileOutputStream(file);
            byte[] buffer = new byte[4096];
            int length;
            while ((length = is.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
            return file;
        } catch (IOException e) {
            return null;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                }
            }
        }
    }

js

// 下载模板
export function downloadTemplate(id) {
  return request({
    url: '/system/person-template/downloadTemplate/'+id,
    method: 'get',
    responseType: 'blob'
  })
}

vue

    //下载
    handleDownload(row) {
      downloadTemplate(row.id).then(response => {
        if (row.templateType == "Word") {
          this.$download.word(response, row.name + ".docx");
        } else if (row.templateType == "Excel") {
          this.$download.excel(response, row.name + ".xls");
        }
      });
    },

34、nginx开启gzip

        gzip on;                      # 是否开启gzip
        gzip_buffers 32 4K;           # 缓冲(压缩在内存中缓冲几块? 每块多大?)
        gzip_comp_level 6;            # 推荐6压缩级别(级别越高,压的越小,越浪费CPU计算资源)
        gzip_min_length 1k;           # 开始压缩的最小长度
        gzip_types text/plain application/javascript text/css application/xml text/xml;  # 对哪些类型的文件用压缩 如txt,xml,html ,css
        gzip_disable "MSIE [1-6]\.";  # 配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
        gzip_http_version 1.0;        # 开始压缩的http协议版本开始压缩的http协议版本
        gzip_vary on;                 # 是否传输gzip压缩标志

35、double运算精度缺失

 		//传入字符串形式的值作为参数,可以确保运算精度
        //创建
        BigDecimal a = BigDecimal.valueOf(10);
        BigDecimal b = new BigDecimal(10);
        BigDecimal c = new BigDecimal("0.1111111");

        System.out.println("加法:"+a.add(b));
        System.out.println("减法:"+a.subtract(b));
        System.out.println("乘法:"+a.multiply(b));
        //10:小数点后的位数 RoundingMode.HALF_DOWN:取舍模式
        System.out.println("除法:"+a.divide(b,10, RoundingMode.HALF_DOWN));
        System.out.println("设置小数点后两位:"+c.setScale(2,BigDecimal.ROUND_HALF_UP));
NumberUtil.round(taxAmount, 2).doubleValue()

36、vue 强制更新表格list对象值

      getServiceListPersonPageTips(this.queryParams).then(response => {
        var tagJson = response.data;
        var listNew = [...this.list];
        for (let i = 0; i < listNew.length; i++) {
          var item = listNew[i];
          let employeeId = item.employeeId;
          let tags = tagJson[employeeId + ""];
          if (tags == undefined) {
            continue;
          }
          this.$set(this.list[i], "tags", tags);
        }
      });

37、el-checkbox 多选框的change 事件自定义传参

<el-checkbox
            v-for="(item, index) in item2.children"
            :checked="checked(item.id)"
            @change="checked => checkChange(checked, item)"

          >{{ formatLabel(item)}}</el-checkbox>

38、jar包启动内存不够:Native memory allocation (mmap) failed to map 354418688 bytes for committing reserved memory.

# 查看内存占用情况汇总
free -m
free -g

# 查看内存占用情况明细
top
M

# 查看指定进程的详细信息
ps -p pid -o pid,ppid,cmd,%cpu,%mem

# jar包启动限制内存占用率,启动内存为512M,最大运行内存为1024M
nohup java -jar -Xms512m -Xmx1024m /home/module-infra-biz.jar

39、el-input 只允许输入数值,最多两位小数

<el-input v-model="form.socialSecurityPayBase" placeholder="请输入社保统一缴纳基数" oninput="value=value.replace(/^\D*([0-9]\d*\.?\d{0,2})?.*$/,'$1')"></el-input>

40、封装子组件,数值组件,允许为负数

//父组件中
<NumberInput v-model.value="item.value" />


//子组件
<template>
  <el-input-number
    v-model="value"
    :step="0.01"
    class="num_input"
    @input.native="changeInputPt2($event)"
    style="width: 100%;"
  ></el-input-number>
</template>

<script>
export default {
  props: {
    value: {
      type: Number,
      default: undefined
    }
  },
  data() {
    return {
    };
  },
  watch: {
    value(newVal) {
      this.$emit('input', newVal);
    }
  },
  methods: {
    changeInputPt2(e) {
      if (e.target.value.indexOf(".") >= 0) {
        e.target.value = e.target.value.substring(
          0,
          e.target.value.indexOf(".") + 3
        ); // 这里采用截取 既可以限制第三位小数输入 也解决了数字输入框默认四舍五入问题
      }
    }
  }
};
</script>


<style scoped>
.num_input /deep/.el-input-number__decrease {
  display: none !important;
}

.num_input /deep/.el-input-number__increase {
  display: none !important;
}

.num_input /deep/.el-input__inner {
  padding: 0 !important;
  text-align: left;
  padding-left: 5px !important;
}
</style>

41、动态下拉框某项禁止选择

<el-select
                  :class="styleCss"
                  :popper-append-to-body="false"
                  multiple
                  style="width: 100%;"
                  v-model="item.invoiceDetailType"
                  placeholder="请选择发票明细类别"
                  @visible-change="filterInvoiceDetailTypes(form)"
                >
                  <el-option
                    v-for="dict in invoiceDetailTypes"
                    :key="dict.value"
                    :label="dict.label"
                    :value="dict.value"
                    :disabled="dict.disabled"
                  />
                </el-select>
filterInvoiceDetailTypes(form) {
      var useInvoiceDetailTypeList = [];
      for (let i = 0; i < form.invoiceDetails.length; i++) {
        const element = form.invoiceDetails[i];
        useInvoiceDetailTypeList = useInvoiceDetailTypeList.concat(
          element.invoiceDetailType
        );
      }
      for (let j = 0; j < this.invoiceDetailTypes.length; j++) {
        const element = this.invoiceDetailTypes[j];
        if (useInvoiceDetailTypeList.indexOf(element.value) > -1) {
          this.$set(this.invoiceDetailTypes[j], "disabled", true);
        } else {
          this.$set(this.invoiceDetailTypes[j], "disabled", false);
        }
      }
      this.$forceUpdate();
    },
// 初始化
this.invoiceDetailTypes = this.getDictDatas("invoice_detail_type");
      for (let i = 0; i < this.invoiceDetailTypes.length; i++) {
        const element = this.invoiceDetailTypes[i];
        element.disabled = false;
      }

42、点击一级菜单自动打开第一个子集有效菜单(顶部为一级菜单模式)

/src/components/TopNav/index.vue中

<!-- el-menu添加点击事件 -->
<el-menu
    :default-active="activeMenu"
    mode="horizontal"
    @select="handleSelect"
    class="topnav"
    :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
  >


// 菜单选择事件
    handleSelect(key, keyPath) {
      this.currentIndex = key;
      const route = this.routers.find(item => item.path === key);
      if (this.ishttp(key)) {
        // http(s):// 路径新窗口打开
        window.open(key, "_blank");
      } else if (!route || !route.children) {
        // 没有子路由路径内部打开
        this.$router.push({ path: key });
        this.$store.dispatch("app/toggleSideBarHide", true);
      } else {
        // 显示左侧联动菜单
        this.activeRoutes(key);
        if (!this.$route.meta.link) {
          this.$store.dispatch("app/toggleSideBarHide", false);
        }
        //判断如果有子集路由,自动打开子集第一个菜单
        if (this.routesSun && this.routesSun.length > 0) {
          let toPath = this.routesSun[0].path;
          if (this.routesSun[0].children && this.routesSun[0].children.length > 0) {
            toPath += "/" + this.routesSun[0].children[0].path;
          }

          this.$router.push({
            path: toPath
          });
        }
      }
    },



// 当前激活的路由
    activeRoutes(key) {
      const routes = [];
      if (this.childrenMenus && this.childrenMenus.length > 0) {
        this.childrenMenus.map(item => {
          if (
            key === item.parentPath ||
            (key === "index" && "" === item.path)
          ) {
            routes.push(item);
          }
        });
      }
      if (routes.length > 0) {
        this.$store.commit("SET_SIDEBAR_ROUTERS", routes);
      } else {
        this.$store.dispatch("app/toggleSideBarHide", true);
      }
      this.routesSun = routes;
    },

Logo

快速构建 Web 应用程序

更多推荐