站长信息
jeffery.xu
jeffery.xu

软件工程师

欢迎访问我的个人笔记网站!我是一名热爱技术的开发者,专注于Web开发和技术分享。

811495111@qq.com
18521510875
筛选

个人笔记

如何使用 WSL(Windows Subsystem for Linux)
编程技巧

先决条件

必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 Windows 11 才能使用以下命令。 如果使用的是早期版本,请参阅 手动安装页

安装 WSL 命令

现在,可以使用单个命令安装运行 WSL 所需的所有内容。 右键单击并选择“以管理员身份运行”,在 管理员 模式下打开 PowerShell 或 Windows 命令提示符,输入 wsl --install 命令,然后重新启动计算机。

PowerShell
wsl --install

此命令将启用运行 WSL 并安装 Linux 的 Ubuntu 分发所需的功能。 (可以更改此默认分布)。

如果运行的是较旧的版本,或者只是不想使用安装命令,并且想要分步说明,请参阅 适用于旧版本的 WSL 手动安装步骤

首次启动新安装的 Linux 分发版时,控制台窗口将打开,系统会要求你等待文件取消压缩并存储在计算机上。 所有未来的发射应该需要不到一秒钟的时间。

知识库数据库设计
编程技巧
-- 用户表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100) UNIQUE,
    nickname VARCHAR(50),
    avatar VARCHAR(255),
    status TINYINT DEFAULT 1 COMMENT '1:正常 0:禁用',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 角色表
CREATE TABLE roles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    role_name VARCHAR(50) UNIQUE NOT NULL,
    role_code VARCHAR(50) UNIQUE NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 用户角色关联表
CREATE TABLE user_roles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id),
    UNIQUE KEY uk_user_role (user_id, role_id)
);

-- 知识分类/目录表
CREATE TABLE categories (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    parent_id BIGINT DEFAULT 0 COMMENT '父分类ID,0为顶级分类',
    sort_order INT DEFAULT 0,
    description TEXT,
    icon VARCHAR(100),
    status TINYINT DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 知识文章表
CREATE TABLE articles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(200) NOT NULL,
    content LONGTEXT,
    summary TEXT,
    category_id BIGINT,
    author_id BIGINT NOT NULL,
    view_count INT DEFAULT 0,
    like_count INT DEFAULT 0,
    status TINYINT DEFAULT 1 COMMENT '1:发布 0:草稿 -1:删除',
    is_top TINYINT DEFAULT 0 COMMENT '是否置顶',
    tags VARCHAR(500),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(id),
    FOREIGN KEY (author_id) REFERENCES users(id)
);

-- 附件表
CREATE TABLE attachments (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    article_id BIGINT,
    file_name VARCHAR(255) NOT NULL,
    file_path VARCHAR(500) NOT NULL,
    file_size BIGINT,
    file_type VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (article_id) REFERENCES articles(id)
);
java知识库网站初步设想
编程技巧

前后端分离 + Spring Boot + MyBatis + MySQL 暂定的项目架构:

## 后端技术栈

```xml
<!-- 主要依赖 -->
Spring Boot 3.x
├── Spring Security (认证授权)
├── MyBatis Plus (数据访问层)
├── MySQL 8.0 (数据库)
├── Redis (缓存)
├── JWT (Token认证)
└── Swagger (API文档)
```

## 项目结构

```
knowledge-base-backend/
├── src/main/java/com/example/kb/
│   ├── config/          # 配置类
│   ├── controller/      # 控制器
│   ├── service/         # 业务层
│   ├── mapper/          # MyBatis映射接口
│   ├── entity/          # 实体类
│   ├── dto/             # 数据传输对象
│   ├── vo/              # 视图对象
│   ├── common/          # 公共类
│   └── utils/           # 工具类
├── src/main/resources/
│   ├── mapper/          # MyBatis XML映射文件
│   ├── application.yml  # 配置文件
│   └── static/          # 静态资源
└── pom.xml
```

## Maven依赖配置

```xml
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <!-- MyBatis Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.4</version>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    
    <!-- Swagger -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.2.0</version>
    </dependency>
</dependencies>
```

## 核心实体类设计

### User实体类
```java
@Data
@TableName("users")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    private String password;
    private String email;
    private String nickname;
    private String avatar;
    private Integer status;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;
}
```

### Article实体类
```java
@Data
@TableName("articles")
public class Article {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String title;
    private String content;
    private String summary;
    private Long categoryId;
    private Long authorId;
    private Integer viewCount;
    private Integer likeCount;
    private Integer status;
    private Integer isTop;
    private String tags;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;
}
```

## MyBatis配置

### application.yml
```yaml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/knowledge_base?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
    
  redis:
    host: localhost
    port: 6379
    database: 0

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.kb.entity
```

## 前端技术栈

### 前台用户端
```json
{
  "技术栈": {
    "框架": "Vue 3 + Vite",
    "UI库": "Element Plus",
    "路由": "Vue Router 4", 
    "状态管理": "Pinia",
    "HTTP客户端": "Axios",
    "编辑器": "Vditor/TinyMCE"
  }
}
```

### 后台管理端
```json
{
  "技术栈": {
    "框架": "Vue 3 + Vite",
    "UI库": "Element Plus",
    "模板": "vue-element-plus-admin",
    "图表": "ECharts",
    "表格": "Element Plus Table"
  }
}
```

## API接口设计示例

### 文章相关接口
```java
@RestController
@RequestMapping("/api/articles")
public class ArticleController {
    
    @GetMapping("/list")
    public R<IPage<ArticleVO>> getArticleList(@RequestParam Map<String, Object> params) {
        // 分页查询文章列表
    }
    
    @GetMapping("/{id}")
    public R<ArticleVO> getArticleById(@PathVariable Long id) {
        // 获取文章详情
    }
    
    @PostMapping
    public R<Void> saveArticle(@RequestBody ArticleDTO articleDTO) {
        // 新增文章
    }
    
    @PutMapping("/{id}")
    public R<Void> updateArticle(@PathVariable Long id, @RequestBody ArticleDTO articleDTO) {
        // 更新文章
    }
}
```

## 开发建议

1. **数据库设计**:先创建数据库表结构
2. **后端开发**:从Entity → Mapper → Service → Controller的顺序开发
3. **前端开发**:先搭建基础框架,再开发具体页面
4. **接口联调**:使用Swagger进行接口测试

需要我提供具体的某个模块的详细代码吗?比如用户认证、文章管理等。

WSL上安装java
编程技巧

# 下载Oracle JDK 24
wget https://download.oracle.com/java/24/latest/jdk-24_linux-x64_bin.tar.gz

# 解压到/opt目录
sudo tar -xzf jdk-24_linux-x64_bin.tar.gz -C /opt/

nano ~/.bashrc

export JAVA_HOME=/opt/jdk-24.0.2/
export JAVA_BIN=$JAVA_HOME/bin
export JAVA_LIB=$JAVA_HOME/lib
export CLASSPATH=.:$JAVA_LIB/tools.jar:$JAVA_LIB/dt.jar
export PATH=$JAVA_BIN:$PATH

source ~/.bashrc

source:Shell内置命令,用于执行脚本文件中的命令
.bashrc:Bash配置文件,包含用户的Shell环境设置
作用
重新加载当前用户的Bash配置文件,使新的环境变量和设置立即生效,而无需重新登录或重启终端。

# 检查环境变量
echo $JAVA_HOME
echo $PATH

# 检查Java是否可用
java -version

sudo apt update && sudo apt upgrade -y 解释
编程技巧

sudo:以管理员权限执行
apt:Ubuntu/Debian的包管理器
update:更新软件包列表(不安装软件,只更新可用软件的信息)
&&:逻辑与,前面命令成功后才执行后面的命令
upgrade -y:升级所有已安装的软件包到最新版本,-y表示自动确认所有提示
为什么更新了很多东西
WSL的Ubuntu系统预装了很多基础软件包:


Python:系统自带Python,很多Linux工具依赖它
系统库:各种系统级别的库文件
开发工具:编译器、构建工具等
网络工具:curl、wget等
这是正常的吗?
是的,这是完全正常的。首次运行时通常会更新:


100-300个软件包
包括Python、系统库、安全补丁等
确保系统处于最新和安全的状态
对你的Java开发影响
更新这些包是好事:


确保系统安全性
获得最新的开发工具
为安装Java环境提供更好的基础

快速进入环境变量
编程技巧

按Win + R键,输入sysdm.cpl
选择"高级"选项卡 → "环境变量"

EF 先model生成到数据库
编程技巧
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace WebNote.Migrations
{
    /// <inheritdoc />
    public partial class AddSiteOwnerTable : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "SiteOwners",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
                    Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
                    Bio = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
                    Resume = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    AvatarPath = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: true),
                    Email = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
                    Mobile = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
                    GitHub = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
                    QQ = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
                    CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
                    IsActive = table.Column<bool>(type: "bit", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_SiteOwners", x => x.Id);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "SiteOwners");
        }
    }
}

Add-Migration AddSiteOwnerTable

Update-Database

Host is not allowed to connect to this MySQL server 解决办法
编程技巧

最近研究mysql,遇到这个问题

解决方法,用navicat加上用户,以及ip

Json.ToJson乱码解决
编程技巧

  // 使用JsonConvert.SerializeObject保留中文
  var jsonSettings = new JsonSerializerSettings
  {
      StringEscapeHandling = StringEscapeHandling.Default
  };
  
  data.RequestBody = JsonConvert.SerializeObject(resultJson, jsonSettings);
  
  // 这里是关键 - 使用JsonConvert.SerializeObject来序列化整个data对象
  string postData = JsonConvert.SerializeObject(data, jsonSettings);
  
  // 使用序列化后的字符串而不是JSON.ToJSON(data)

 var content = HttpTools.PostHttpNHSHelp(url, postData, "同步答卷结果");

bundler压缩出现问题
编程技巧

问题出在函数声明的位置和压缩工具处理函数声明的方式上。
在JavaScript中,函数声明提升是很常见的,但是在压缩过程中,当函数声明放在代码中间而不是顶部时,可能会导致问题。

采用将 togglePanel 改为函数表达式的方法

document.addEventListener('DOMContentLoaded', function () {
    // 获取可折叠面板的标题元素
    const collapsibleHeader = document.querySelector('.collapsible-header');
    const collapseElement = document.getElementById('relatedNotesCollapse');
    const toggleIcon = document.querySelector('.toggle-icon');
    const mainContent = document.querySelector('.col-md-8');
    const sidePanel = document.querySelector('.col-md-4');
    const noteContent = document.querySelector('.note-content-detail');
    const floatBtn = document.querySelector('.float-toggle-btn');

    if (!collapsibleHeader || !collapseElement || !toggleIcon || !mainContent || !sidePanel) {
        // 如果找不到必要的元素,就退出
        return;
    }

    // 切换面板状态的函数(使用函数表达式而不是函数声明)
    const togglePanel = function() {
        const isExpanded = !sidePanel.classList.contains('collapsed');

        // 只在非移动设备上更新状态
        if (window.innerWidth >= 768) {
            // 更新本地存储中的状态
            localStorage.setItem('relatedNotesCollapsed', isExpanded);

            if (isExpanded) {
                // 收缩
                sidePanel.classList.add('collapsed');
                mainContent.classList.add('expanded');

                // 隐藏内容区域
                collapseElement.classList.remove('show');

                // 显示悬浮按钮
                if (floatBtn) {
                    floatBtn.classList.add('visible');
                }
            } else {
                // 展开
                sidePanel.classList.remove('collapsed');
                mainContent.classList.remove('expanded');

                // 显示内容区域
                collapseElement.classList.add('show');

                // 隐藏悬浮按钮
                if (floatBtn) {
                    floatBtn.classList.remove('visible');
                }
            }
        }
    };

    // 从本地存储中获取面板状态(如果有)
    const isCollapsed = localStorage.getItem('relatedNotesCollapsed') === 'true';

    // 初始化面板状态
    if (isCollapsed && window.innerWidth >= 768) {
        // 只在非移动设备上应用收缩状态
        // 收缩
        sidePanel.classList.add('collapsed');
        mainContent.classList.add('expanded');

        // 隐藏内容区域
        collapseElement.classList.remove('show');

        // 显示悬浮按钮
        if (floatBtn) {
            floatBtn.classList.add('visible');
        }
    }

    // 添加点击事件监听器,仅在非移动设备下启用
    collapsibleHeader.addEventListener('click', function (e) {
        // 在移动设备下阻止叉号图标的点击事件
        if (window.innerWidth < 768 && e.target.classList.contains('toggle-icon')) {
            e.stopPropagation();  // 阻止事件传播
            return;  // 在移动设备上不处理叉号图标点击
        }

        // 非移动设备下正常切换面板
        if (window.innerWidth >= 768) {
            togglePanel();
        }
    });

    // 特别为移动设备处理叉号图标
    if (toggleIcon) {
        toggleIcon.addEventListener('click', function (e) {
            // 在移动设备下阻止点击事件
            if (window.innerWidth < 768) {
                e.stopPropagation();
                e.preventDefault();
            }
        });
    }

    // 如果存在悬浮按钮,添加点击事件
    if (floatBtn) {
        floatBtn.addEventListener('click', function () {
            togglePanel();
        });
    }

    // 窗口大小改变时的处理
    window.addEventListener('resize', function () {
        if (window.innerWidth < 768) {
            // 在移动设备视图下恢复正常布局
            sidePanel.classList.remove('collapsed');
            mainContent.classList.remove('expanded');

            // 显示相关笔记内容
            collapseElement.classList.add('show');

            // 隐藏悬浮按钮
            if (floatBtn) {
                floatBtn.classList.remove('visible');
            }

            // 确保叉号图标在移动设备上不执行收缩功能
            if (toggleIcon) {
                toggleIcon.style.pointerEvents = 'none';
                toggleIcon.style.display = 'none';
            }
        } else {
            // 恢复图标功能
            if (toggleIcon) {
                toggleIcon.style.pointerEvents = 'auto';
                toggleIcon.style.display = '';
            }

            // 在桌面视图下,如果状态是收缩的,保持收缩
            if (localStorage.getItem('relatedNotesCollapsed') === 'true') {
                sidePanel.classList.add('collapsed');
                mainContent.classList.add('expanded');
                collapseElement.classList.remove('show');
                if (floatBtn) {
                    floatBtn.classList.add('visible');
                }
            }
        }
    });

    // 初始检查是否为移动设备,禁用叉号图标功能
    if (window.innerWidth < 768 && toggleIcon) {
        toggleIcon.style.pointerEvents = 'none';
        toggleIcon.style.display = 'none';
    }
});