redis+mysql生成商品的唯一货号 带自动递增

如果redis数据被丢失,则从数据库查询出最大数值保存到redis在自增

  // 生成递增的货号逻辑
    public String generateProductCode() {
        lock.lock();  // 加锁,确保多线程安全
        try {
            // 从 Redis 获取当前货号,如果 Redis 中不存在,初始化货号
            String currentCodeStr = redisTemplate.opsForValue().get(PRODUCT_CODE_KEY);
            if (currentCodeStr == null) {
                // 从数据库中获取当前最大的货号
                Integer maxCode = productMapper.findMaxProductCode();
                if (maxCode == null) {
                    maxCode = 0;  // 如果数据库中没有数据,从 0 开始
                }
                // 初始化 Redis 中的货号为数据库中的最大值
                redisTemplate.opsForValue().set(PRODUCT_CODE_KEY, maxCode.toString());
            }

            // 使用 Redis 的 INCR 命令自增货号
            Long newCode = redisTemplate.opsForValue().increment(PRODUCT_CODE_KEY);
            return String.format("%08d", newCode);  // 格式化为 8 位数字
        } finally {
            lock.unlock();  // 解锁
        }
    }
 // 生成递增的货号
        String productCode = generateProductCode();
        product.setItemNumberPrefix("ZML");
        product.setItemNumber(productCode);
 private static final String PRODUCT_CODE_KEY = "product:code";  // Redis 中货号的 key
private final Lock lock = new ReentrantLock();  // 创建一个 ReentrantLock 锁

    @Autowired
    private StringRedisTemplate redisTemplate;
 // 查询当前数据库中最大的货号
    @Select("SELECT MAX(p.item_number) FROM Product p")
    Integer findMaxProductCode();

为了方便查询我是把编号和编号前缀分成了两个字段保存

private String itemNumberPrefix;
@ApiModelProperty(value = "商品货号")
private String itemNumber;

百度UEditor调试

提示后端配置有误的解决方案

package com.honghan.site.controller;

import com.alibaba.fastjson2.JSON;
import com.baidu.ueditor.ActionEnter;
import com.honghan.resource.api.RemoteFileService;
import com.honghan.resource.api.domain.SysFile;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.poi.util.IOUtils;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@RequestMapping("/ueditor")
@RestController
public class UeditorController {


    @DubboReference
    private RemoteFileService remoteFileService;
    //配置ueditor后端上传接口的验证
    @RequestMapping("/config")
    public String config(HttpServletRequest request, HttpServletResponse response, String action, MultipartFile[] upfile) throws IOException {
         //获取配置文件json内容返回给前端
        if (action.equals("config")) {
            request.setCharacterEncoding("utf-8");
            response.setHeader("Content-Type", "text/html");
            //注意path不能含有中文路径,确认该path能指向config.json
            String path = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "config";
            System.out.println("path = " + path);
            PrintWriter printWriter = response.getWriter();
            printWriter.write(new ActionEnter(request, path).exec());
            printWriter.flush();
            printWriter.close();
        }
        //上传图片
        else if (action.equals("uploadimage")) {
            Map<String, Object> result = new HashMap<String, Object>();
            for (MultipartFile multipartFile : upfile) {
                //调用系统写好的文件上传方法,并返回给前端上传后的文件路径,组件需要拼接回显
                SysFile sysFile = remoteFileService.upload(multipartFile.getName(),
                    multipartFile.getOriginalFilename(), multipartFile.getContentType(), multipartFile.getBytes());
                result.put("title", sysFile);
                result.put("original", multipartFile.getOriginalFilename());
                result.put("state", "SUCCESS");
                result.put("url", sysFile.getUrl());
                String jStr = JSON.toJSONString(result);
                return jStr;
            }
            return null;
        }
        //上传视频
        else if(action.equals("uploadvideo")){
            Map<String, Object> result = new HashMap<String, Object>();
            for (MultipartFile multipartFile : upfile) {
                SysFile sysFile = remoteFileService.upload(multipartFile.getName(),
                    multipartFile.getOriginalFilename(), multipartFile.getContentType(), multipartFile.getBytes());
                result.put("title", sysFile);
                result.put("original", multipartFile.getOriginalFilename());
                result.put("state", "SUCCESS");
                result.put("url", sysFile.getUrl());
                String jStr = JSON.toJSONString(result);
                return jStr;
            }
        }else{
            Map<String, Object> result = new HashMap<String, Object>();
            for (MultipartFile multipartFile : upfile) {
                SysFile sysFile = remoteFileService.upload(multipartFile.getName(),
                    multipartFile.getOriginalFilename(), multipartFile.getContentType(), multipartFile.getBytes());
                result.put("title", sysFile);
                result.put("original", multipartFile.getOriginalFilename());
                result.put("state", "SUCCESS");
                result.put("url", sysFile.getUrl());
                String jStr = JSON.toJSONString(result);
                return jStr;
            }
        }
        return null;
    }
}

config.json文件

    {
      "imageActionName": "uploadimage", /* 执行上传图片的action名称 */
      "imageFieldName": "upfile", /* 提交的图片表单名称 */
      "imageMaxSize": 2048000, /* 上传大小限制,单位B */
      "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
      "imageCompressEnable": true, /* 是否压缩图片,默认是true */
      "imageCompressBorder": 1600, /* 图片压缩最长边限制 */
      "imageInsertAlign": "none", /* 插入的图片浮动方式 */
      "imageUrlPrefix": "", /* 图片访问路径前缀 */
      "imagePathFormat": "image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
      /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
      /* {time} 会替换成时间戳 */                                /* {yyyy} 会替换成四位年份 */
      /* {yy} 会替换成两位年份 */
      /* {mm} 会替换成两位月份 */
      /* {dd} 会替换成两位日期 */
      /* {hh} 会替换成两位小时 */
      /* {ii} 会替换成两位分钟 */
      /* {ss} 会替换成两位秒 */
      /* 非法字符 \ : * ? " < > | */
      /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */
      /* 涂鸦图片上传配置项 */
      "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */
      "scrawlFieldName": "upfile", /* 提交的图片表单名称 */
      "scrawlPathFormat": "image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */
      "scrawlUrlPrefix": "", /* 图片访问路径前缀 */
      "scrawlInsertAlign": "none",    /* 截图工具上传 */
      "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */
      "snapscreenPathFormat": "image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      "snapscreenUrlPrefix": "", /* 图片访问路径前缀 */
      "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */
      /* 抓取远程图片配置 */
      "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
      "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */
      "catcherFieldName": "source", /* 提交的图片列表表单名称 */
      "catcherPathFormat": "image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      "catcherUrlPrefix": "", /* 图片访问路径前缀 */
      "catcherMaxSize": 2048000, /* 上传大小限制,单位B */
      "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */
      /* 上传视频配置 */
      "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */
      "videoFieldName": "upfile", /* 提交的视频表单名称 */
      "videoPathFormat": "video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      "videoUrlPrefix": "http://localhost:81/dev-api/", /* 视频访问路径前缀 */
      "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */
      "videoAllowFiles": [        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */
      /* 上传文件配置 */
      "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */
      "fileFieldName": "upfile", /* 提交的文件表单名称 */
      "filePathFormat": "file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
      "fileUrlPrefix": "", /* 文件访问路径前缀 */
      "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */
      "fileAllowFiles": [        ".png", ".jpg", ".jpeg", ".gif", ".bmp",        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",        ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",        ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"    ], /* 上传文件格式显示 */
      /* 列出指定目录下的图片 */
      "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */
      "imageManagerListPath": "image/", /* 指定要列出图片的目录 */
      "imageManagerListSize": 20, /* 每次列出文件数量 */
      "imageManagerUrlPrefix": "", /* 图片访问路径前缀 */
      "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */
      "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */
      /* 列出指定目录下的文件 */
      "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */
      "fileManagerListPath": "file/", /* 指定要列出文件的目录 */
      "fileManagerUrlPrefix": "", /* 文件访问路径前缀 */
      "fileManagerListSize": 20, /* 每次列出文件数量 */
      "fileManagerAllowFiles": [        ".png", ".jpg", ".jpeg", ".gif", ".bmp",        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",        ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",        ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"    ] /* 列出的文件类型 */
    }
    

What's more: 服务端需要做的配置

配置完上述内容后,控制台可能会出现"后台配置项返回格式出错,上传功能将不能正常使用!"的报错,
我们在编辑器中上传图片或者视频,也会出现响应的报错,这是因为没有配置服务器的请求接口,在ueditor.config.js中,对serverUrl进行配置:

// 服务器统一请求接口路径
, serverUrl: 'http://xxx/ueditor/config'
//地址管你们后端要去

MapStructPlus 报错整理

1."Couldn't retrieve @Mapper annotation" 异常

该异常是因为 MapStruct 依赖冲突导致的,由于 MapStructPlus 中已经依赖了 MapStruct,所以在使用时无需再添加 MapStruct 的依赖。 同时,建议其它依赖中的 MapStruct,也建议排除掉,比如 springfox-swagger2 中就依赖了 MapStruct。

排除完依赖后,重新执行 clean compile

2.两个类型不一致
可用注解:

@AutoMapping(target = "menuIds", expression = "java(com.honghan.common.core.utils.StringUtils.join(source.getMenuIds(), \",\"))")

例如类型:
private Long[] menuIds

private String menuIds;
类型

3.pom 的build设置

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.verison}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>com.github.therapi</groupId>
                            <artifactId>therapi-runtime-javadoc-scribe</artifactId>
                            <version>0.15.0</version>
                        </path>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                            <version>${spring-boot.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>io.github.linpeilie</groupId>
                            <artifactId>mapstruct-plus-processor</artifactId>
                            <version>${mapstruct-plus.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>${mapstruct-plus.lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <!-- 单元测试使用 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                    <!-- 根据打包环境执行对应的@Tag测试方法 -->
                    <groups>${profiles.active}</groups>
                    <!-- 排除标签 -->
                    <excludedGroups>exclude</excludedGroups>
                </configuration>
            </plugin>
            <!-- 统一版本号管理 -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>flatten-maven-plugin</artifactId>
                <version>${flatten-maven-plugin.version}</version>
                <configuration>
                    <updatePomFile>true</updatePomFile>
                    <flattenMode>resolveCiFriendliesOnly</flattenMode>
                </configuration>
                <executions>
                    <execution>
                        <id>flatten</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>flatten</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>flatten.clean</id>
                        <phase>clean</phase>
                        <goals>
                            <goal>clean</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.spotify</groupId>
                    <artifactId>docker-maven-plugin</artifactId>
                    <version>${docker.plugin.version}</version>
                    <configuration>
                        <imageName>${docker.namespace}/${project.artifactId}:${project.version}</imageName>
                        <dockerDirectory>${project.basedir}</dockerDirectory>
                        <dockerHost>${docker.registry.host}</dockerHost>
                        <registryUrl>${docker.registry.url}</registryUrl>
                        <serverId>${docker.registry.url}</serverId>
                        <resources>
                            <resource>
                                <targetPath>/</targetPath>
                                <directory>${project.build.directory}</directory>
                                <include>${project.build.finalName}.jar</include>
                            </resource>
                        </resources>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!-- 关闭过滤 -->
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/webapp/</directory>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <!-- 引入所有 匹配文件进行过滤 -->
                <includes>
                    <include>application*</include>
                    <include>bootstrap*</include>
                    <include>logback*</include>
                </includes>
                <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

seata注册到nacos中

nacos 2.1.1
seata 1.6.1

seata配置文件

server:
  port: 7091

spring:
  application:
    name: ruoyi-seata-server
  profiles:
    # 环境配置
    active: zhongxinlian

logging:
  config: classpath:logback-spring.xml
  file:
    path: ./logs/seata
#  extend:
#    logstash-appender:
#      destination: 127.0.0.1:4560
#    kafka-appender:
#      bootstrap-servers: 127.0.0.1:9092
#      topic: logback_to_logstash

console:
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
       #我发现这里只能填写对应的nacos命名空间的id,填名称会对应不上,无法注册
      namespace: 373e77cf-a511-4d82-866e-629d5979097b
      username: nacos
      password: g6ySGnapptZ7EjD666
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
      data-id: seata-server.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    nacos:
      application: ruoyi-seata-server
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      #我发现这里只能填写对应的nacos命名空间的id,填名称会对应不上,无法注册
      namespace: 373e77cf-a511-4d82-866e-629d5979097b  
      cluster: default
      username: nacos
      password: g6ySGnapptZ7EjD666
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
  security:
    secretKey:
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login