1.限流处理 @RateLimiter
@PostMapping("/createOrder")
@ApiOperation("创建充值订单")
@RateLimiter(key = CacheConstants.REPEAT_SUBMIT_KEY,time = 10,count = 1,limitType = LimitType.IP)
public R createOrder(@RequestBody Form form) {
//业务处理
return R.ok(order.getOrderNo());
}
2.@RepeatSubmit 防止重复提交
@RepeatSubmit
@PostMapping("/createOrder")
@ApiOperation("创建充值订单")
public R createOrder(@RequestBody TCommissionOrderForm form) {
//业务处理
return R.ok(order.getOrderNo());
}
3.数据字典使用
(1)定义
export default {
dicts: ['dai_li', 'sys_yes_no'],
(2)列表获取
<el-table-column label="审核状态" align="center" prop="status">
<template v-slot:="scope">
<dict-tag :options="dict.type.sys_yes_no" :value="scope.row.status"/>
</template>
</el-table-column>
(3)查询/新增/编辑
<el-form-item label="等级" prop="agentGrade">
<el-select v-model="queryParams.agentGrade" placeholder="请选择代理等级" clearable>
<el-option
v-for="dict in dict.type.dai_li"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
(4)js内获取
typeFormat(agentGrade) {
var that=this;
this.dict.type.dai_li.forEach(function (i){
if (i.value==agentGrade){
that.form.agentGradeFy=i.label;
}
})
},
4.图片
limit限制上传图片数量
<el-form-item label="图片" prop="coverUrl">
<image-upload :limit="1" v-model="form.coverUrl" :width="80" :height="80"/>
</el-form-item>
5.下拉框长度比输入框短的处理办法
style=“width: 100%”
<el-form-item label="管理者性别" prop="sex">
<el-select v-model="form.sex" style="width: 100%" disabled placeholder="请选择管理者性别">
<el-option
v-for="dict in dict.type.sys_user_sex"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
6.打开一个新的弹窗列表
(1)父页面
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button
size="mini"
type="text"
icon="el-icon-info"
@click="openImportTable(scope.row)">订单明细</el-button>
</template>
</el-table-column>
<import-table ref="import"/>
<script>
import importTable from "@/views/system/shopOrder/tjIndex"
export default {
components:{importTable},
}
methods: {
/** 打开导入表弹窗 */
openImportTable(row) {
this.$refs.import.show(row);
},
}
</script>
(2)子页面
A.el-dialog 把页面代码包起来
<el-dialog title="订单明细" :visible.sync="visible" width="70%" top="1vh" append-to-body>
</el-dialog>
B.用于父页面调用
methods: {
// 显示弹框
show(row) {
this.shopManageId=row.shopId
this.goodsCommodityId=row.goodsCommodityId
this.getList();
this.visible = true;
},
}
C.完整代码
<template>
<el-dialog title="订单明细" :visible.sync="visible" width="70%" top="1vh" append-to-body>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
<el-form-item label="订单号" prop="orderNo">
<el-input style="width: 155px;" v-model="queryParams.orderNo" placeholder="请输入订单号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="shopOrderList">
<el-table-column label="订单号" align="center" prop="orderNo" width="150" :show-overflow-tooltip="true"/>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<div slot="footer" class="dialog-footer">
<el-button @click="visible = false">关 闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { listShopOrder } from "@/api/system/shopOrder";
export default {
name: "ShopOrder",
dicts: ['t_shop_order_delivery', 'comm_pay', 't_shop_order_status'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 商品订单表格数据
shopOrderList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
visible: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
orderNo: null,
shopManageId: null,
sysUserId: null,
addressId: null,
goodsCommodityId: null,
num: null,
totalPrice: null,
price: null,
orderPrice: null,
status: null,
payWay: null,
outTradeNo: null,
deliveryWay: null,
payTime: null,
cancelTime: null,
fhTime: null,
},
// 表单参数
form: {},
shopManageId:null,
goodsCommodityId:null,
};
},
methods: {
// 显示弹框
show(row) {
this.shopManageId=row.shopId
this.goodsCommodityId=row.goodsCommodityId
this.getList();
this.visible = true;
},
/** 查询商品订单列表 */
getList() {
this.queryParams.shopManageId=this.shopManageId
this.queryParams.goodsCommodityId=this.goodsCommodityId
this.queryParams.status='tj'
this.loading = true;
listShopOrder(this.queryParams).then(response => {
this.shopOrderList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 表单重置
reset() {
this.form = {
id: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
delFlag: null,
orderNo: null,
shopManageId: null,
sysUserId: null,
addressId: null,
goodsCommodityId: null,
num: null,
totalPrice: null,
price: null,
orderPrice: null,
status: null,
payWay: null,
outTradeNo: null,
deliveryWay: null,
payTime: null,
cancelTime: null,
fhTime: null,
remark: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
}
};
</script>
(3)效果
7.偌依免登录访问页面,不影响前端框架正常登录访问,直接用链接免登录访问
(1)SecurityConfig 加入需要访问得接口
.antMatchers("/admin/vel/*").permitAll()
(2)Controller 去掉 @PreAuthorize
(3)src -> router -> index.js
注意:/find/vdlH5 不要跟框架里面的一致,自定义一个即可
// 公共路由
export const constantRoutes = [
{
path: '/find/vdlH5',
component: () => import('@/views/system/find/index'),
hidden: true
},
}
(4)src -> permission.js
白名单加入第三步定义的path:‘/find/vdlH5’
const whiteList = ['/login', '/register','/find/vdlH5']
参考:https://www.cnblogs.com/huashenyin/p/16122113.html
8.springboot升级
https://blog.csdn.net/qq_44403239/article/details/138078084
除了以上步骤之外
(1)升级了两个xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.1.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.6</version>
</dependency>
(2)RepeatSubmitInterceptor
使用jakarta
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
9.自定义文件保存目录
(1)使用 file-url=“apk”
<el-form-item label="apk文件" prop="fileUrl">
<file-upload v-model="form.fileUrl" file-url="apk"/>
</el-form-item>
(2)FileUpload组件
<el-upload :data="fileData">
<script>
props: {
//自定义上传路径
fileUrl:{
type:String,
default:null,
}
},
data() {
return {
fileData:{},
};
},
methods: {
// 上传前校检格式和大小
handleBeforeUpload(file) {
this.fileData.fileUrl=this.fileUrl;
this.$modal.loading("正在上传文件,请稍候...");
this.number++;
return true;
},
}
</script>
(3)后端
/**
* 通用上传请求(单个)
*/
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file, FileForm form) {
try {
// 上传文件路径
String filePath = DCQQConfig.getUploadPath(form.getFileUrl());
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
/**
* 获取上传路径
*/
public static String getUploadPath()
{
return getProfile() + "/upload";
}
/**
* 获取上传路径
* @param fileUrl 自定义路径
* @return
*/
public static String getUploadPath(String fileUrl){
if (StringUtils.isBlank(fileUrl)){
return getUploadPath();
}
return getProfile() + "/"+fileUrl;
}
(4)效果
10.获取用户登录状态
/**
* 安全服务工具类
*
* @author ruoyi
*/
public class SecurityUtils {
/**
* 获取用户登录状态:true已登录,false未登录
**/
public static boolean isLogin() {
Object principal = getAuthentication().getPrincipal();
if (principal.toString().equals("anonymousUser")){
return false;
}
return true;
}
}
11.linux文件上传报413 Payload Too Large
(1)在nginx中加入:client_max_body_size 200M;
参考:https://blog.csdn.net/weixin_43652507/article/details/122130540
12.自定义加载消息
submitForm() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
let ld = Loading.service({ text: "系统处理中,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
update(this.formData).then(response => {
ld.close()
this.$modal.msgSuccess("修改成功");
});
})
}
13.登录单独设置token过期时间
(1)application.yml
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 720
# 前端令牌有效期 30天
appExpireTime: 43200
(2)LoginUser.java
/**
* 类型(0.后端登录,1.前端登录)
*/
private Integer type=0;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
(3)TokenService.java
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
int exTime = getExTime(loginUser.getType());
loginUser.setExpireTime(loginUser.getLoginTime() + exTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, exTime, TimeUnit.MINUTES);
}
/**
* 过期时间获取
*
* @param type
* @return
*/
private int getExTime(Integer type) {
int exTime = 0;
if (type == 0) {//后端
exTime = expireTime;
} else if (type == 1) {//前端
exTime = appExpireTime;
}
return exTime;
}
(4)JwtAuthenticationTokenFilter 登录验证token拦截类
14.Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the ‘-parameters’ flag.
解决办法
1.@RequestParam(name=“tables”)
参考:http://t.csdnimg.cn/gCIzN
15.偌依首页图片获取位置
16.数据监控密码
17. 打开一个新的页面
(1) 父页面
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
<el-table-column label="字典类型" align="left" :show-overflow-tooltip="true">
<template slot-scope="scope">
<router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
<span>{{ scope.row.dictType }}</span>
</router-link>
</template>
</el-table-column>
</el-table>
(2)router–》index.js
a.动态路由
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
},
b.静态路由
{
path: '/mode2/boxPateItem',
component: Layout,
hidden: true,
redirect: 'index',
children: [
{
path: 'index/:boxPeriodId(\\d+)',
component: () => import('@/views/mode2/boxPateItem/index'),
name: 'boxPateItem',
meta: { title: '参与物品', icon: 'boxPateItem' }
}
]
},
(3)子页面
created() {
const dictId = this.$route.params && this.$route.params.dictId;
this.getTypeList();
},
18.数据字典sql自动生成
(1)输入类型编码和说明自动生成sql
public class Test {
public static void main(String[] args) {
String dict_type="product_order_status";
String name_label_value="订单状态:0=未付款,1=已付款(待发货),2=已发货(待收货),3=已签收(已完成),4=退货申请,5=退货中,6=已退货,9=取消交易";
createSql(name_label_value, dict_type);
}
private static void createSql(String name_label_value, String dict_type) {
String dict_name= name_label_value.split(":|:")[0];
String label_value= name_label_value.split(":|:")[1];
String tableType="INSERT INTO sys_dict_type ( dict_name, dict_type, status, create_by, create_time) VALUES ('"+dict_name+"', '"+ dict_type +"', '0', 'admin', SYSDATE());";
System.out.println(tableType);
String[] arr = label_value.split(",|,");
for (int i = 0; i < arr.length; i++) {
String value=arr[i].split("=|\\.")[0];
String label=arr[i].split("=|\\.")[1];
String tableData="INSERT INTO sys_dict_data ( dict_sort, dict_label, dict_value, dict_type, list_class, create_by, create_time) VALUES ("+i+", '"+label+"', '"+value+"', '"+ dict_type +"', 'primary', 'admin', SYSDATE());";
System.out.println(tableData);
}
}
}
(2)直接查询数据库生成
a.表格式
b.代码
package com.gu.web.controller;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
public class Test {
//数据库连接
private static final String url="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai";
//数据库账号
private static final String user="root";
//数据库密码
private static final String password="root";
public static void main(String[] args) {
//以下sql用于辅助快速查询数据库中的表
//SELECT GROUP_CONCAT(table_name) FROM information_schema.`TABLES` WHERE table_name LIKE 't_%'
String tables="t_product,t_product_order";
createSql(tables,2);
}
/**
* 获取数据字典插入sql
* @param tables 表名,多个表用逗号分隔
* @param type 类型(1.需要前缀,2.去掉前缀)
*/
private static void createSql(String tables,int type) {
for (String table : tables.split(",|,")) {
System.out.println("-- "+table);
Connection mysqlConn=null;
Statement mysqlStmt=null;
ResultSet mysqlRs=null;
ResultSet resultSet=null;
String tableComment="";
String dt=table;
if (type==2) {
dt=table.substring(table.indexOf("_")+1);
}
try {
Class.forName("com.mysql.cj.jdbc.Driver");
mysqlConn= DriverManager.getConnection(url,user,password);
mysqlStmt=mysqlConn.createStatement();
mysqlRs=mysqlStmt.executeQuery("select * from "+ table);
ResultSetMetaData mysqlMeda=mysqlRs.getMetaData();
int columnCount=mysqlMeda.getColumnCount();
//获取字段注释map
DatabaseMetaData metaData = mysqlConn.getMetaData();
Map<String, String> columnComments = getColumnComments(metaData, table);
//获取表注释
resultSet = mysqlStmt.executeQuery("Select TABLE_COMMENT COMMENT from INFORMATION_SCHEMA.TABLES Where table_name = '" + table + "'");
while (resultSet.next()) {
tableComment = resultSet.getString("COMMENT");
}
for (int i = 1; i <= columnCount; i++) {
String columnName=mysqlMeda.getColumnName(i);//字段名
String columnComment = columnComments.get(columnName);//字段注释
if (columnComment.contains(":") || columnComment.contains(":")){
pjSql(columnComment,dt+"_"+columnName,tableComment);
}
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
try {
if (mysqlConn!=null){
mysqlConn.close();
}
if (mysqlStmt!=null){
mysqlStmt.close();
}
if (mysqlRs!=null){
mysqlRs.close();
}
if (resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* sql组装
* @param name_label_value 如:订单状态:0=未付款,1=已付款(待发货),2=已发货(待收货),3=已签收(已完成),4=退货申请,5=退货中,6=已退货,9=取消交易
* @param dict_type 如:product_order_status
*/
private static void pjSql(String name_label_value, String dict_type,String tableComment) {
String dict_name= name_label_value.split(":|:")[0];
String label_value= name_label_value.split(":|:")[1];
String tableType="INSERT INTO sys_dict_type ( dict_name, dict_type, status, create_by, create_time) VALUES ('"+tableComment+"-"+dict_name+"', '"+ dict_type +"', '0', 'admin', SYSDATE());";
System.out.println(tableType);
String[] arr = label_value.split(",|,");
for (int i = 0; i < arr.length; i++) {
String value=arr[i].split("=|\\.")[0];
String label=arr[i].split("=|\\.")[1];
String tableData="INSERT INTO sys_dict_data ( dict_sort, dict_label, dict_value, dict_type, list_class, create_by, create_time) VALUES ("+i+", '"+label+"', '"+value+"', '"+ dict_type +"', 'primary', 'admin', SYSDATE());";
System.out.println(tableData);
}
System.out.println();
}
/**获取字段注注释*/
private static Map<String, String> getColumnComments(DatabaseMetaData metaData, String tableName) throws SQLException {
Map<String, String> columnComments = new HashMap<>();
try (ResultSet columns = metaData.getColumns(null, null, tableName, null)) {
while (columns.next()) {
String columnName = columns.getString("column_name");
String columnComment = columns.getString("remarks");
columnComments.put(columnName, columnComment);
}
}
return columnComments;
}
}
19.菜单目录去掉井号可以向前移动
20.vue项目idea提示Module is not installed
21.增加多张图片写法
(1)前端
(2)后端
22.ruoyi的springboot微信小程序登录实现方式
23.代码生成器自动插入数据字典实现
(1)表设计(新增数据字典)
(2)表设计(公共:从已有的数据字典获取)
(2)导入的时候插入数据字典
(3)数据字典效果
(4)代码实现
表gen_table_column增加字段table_name
导入:/tool/gen/importTable
删除:/tool/gen/{{tableIds}}
24.代码生成器自动生成枚举
(1)预览 /tool/gen/preview/{tableId}
(2)代码生成 /tool/gen/batchGenCode
25.代码生成器模板语法记录
(1)定义变量用法
- 定义:#set($Entity=“TreeEntity”)
- 使用:${Entity}
26.RuoYiConfig增加字段