# milkbox2-api
**Repository Path**: guo-yongchen/milkbox2-api
## Basic Information
- **Project Name**: milkbox2-api
- **Description**: 全新架构milkbox
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-12-30
- **Last Updated**: 2025-05-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: SpringBoot3, java21, SaToken, BCrypt, 三员权限管理
## README
# 技术栈
## 主要技术
基础语言(Java 21)
整合框架(SpringBoot 3)
持久层框架(MybatisPlus)
缓存(Redis)
## 辅助技术
简化类(Lombok)
接口文档(SpringDoc)
糊涂工具包(Hutool)
权限认证(SaToken)
密码保护(BCrypt)
# 升级java21的问题
升级到java21以后的启动参数,不加会报错
启动举例
```shell
java21的bin目录/java -jar jar包目录/jar包名称.jar --add-opens java.base/sun.util.calendar=ALL-UNNAMED
```
必选参数
```shell
--add-opens java.base/sun.util.calendar=ALL-UNNAMED
```
这些以后可能会用到
```shell
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/jdk.internal.access=ALL-UNNAMED
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
```
# 打包与启动
## 如何打jar包
在项目根路径下执行maven的package命令,之后会在milkbox-app模块下的target目录下生成一个jar包,可以使用指令启动这个jar包
## 在idea中启动
启动时需要添加环境变量,如下图所示

主要包含的环境变量
```bash
DB_HOST=localhost
DB_PASSWORD=xxxx
DB_PORT=3306
DB_USERNAME=root
```
## 在命令行中启动
启动命令参考
win,注意要使用cmd,不要用powershell
```bash
chcp
chcp 65001
C:\Users\Administrator\.jdks\openjdk-21.0.1\bin\java -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 --add-opens java.base/sun.util.calendar=ALL-UNNAMED -jar .\milkbox-app-1.0-DEV.jar
```
# 新建模块
## 在milkbox-service模块下新建模块后需要配置maven的导包依赖
在项目根目录下的pom.xml的dependencyManagement中定义新模块的版本,然后在milkbox-app模块中引入新模块
# MapStruct
项目建议使用MapStruct来做对象转换。定义一个转换接口与转换方法,MapStruct会根据方法的参数与返回,在启动项目之前的编译阶段值自动生成实现类。
> 注意:如果对接口进行了修改,一定先执行maven的clean命令然后再编译项目。否则新修改的内容可能不会更新编译。
当前其对应接口的实现类生成的源码文件位于`模块.../target/generated-sources/annotations/包路径.../XxxxxxImpl.java`
其实现类的字节码文件将会按照接口的包路径位置生成
写法举例如下:
```java
package top.milkbox.sys.modular.user.mapStruct;
import org.mapstruct.Mapper;
import top.milkbox.sys.modular.user.entity.SysUserEntity;
import top.milkbox.sys.modular.user.param.SysUserAddParam;
import top.milkbox.sys.modular.user.param.SysUserEditParam;
import top.milkbox.sys.modular.user.vo.SysUserVo;
/**
* SysUser相关的实体类之间的转换
* 此接口由MapStruct在编译时生成实现代码,详细解释请看readme
*
* @author milkbox
* @Data 2024-11-19
*/
@Mapper // 注意这个Mapper不是String的Mapper
public interface SysUserMapStruct {
/**
* entity转vo
*/
SysUserVo entityToVo(SysUserEntity entity);
/**
* addParam转entity
*/
SysUserEntity addParamToEntity(SysUserAddParam addParam);
/**
* editParam转entity
*/
SysUserEntity editParamToEntity(SysUserEditParam editParam);
}
```
生成的实现类源码:
```java
package top.milkbox.sys.modular.user.mapStruct;
import javax.annotation.processing.Generated;
import org.springframework.stereotype.Component;
import top.milkbox.sys.modular.user.entity.SysUserEntity;
import top.milkbox.sys.modular.user.param.SysUserAddParam;
import top.milkbox.sys.modular.user.param.SysUserEditParam;
import top.milkbox.sys.modular.user.vo.SysUserVo;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2024-11-19T09:38:00+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 21.0.1 (Oracle Corporation)"
)
@Component
public class SysUserMapStructImpl implements SysUserMapStruct {
@Override
public SysUserVo entityToVo(SysUserEntity entity) {
if ( entity == null ) {
return null;
}
SysUserVo sysUserVo = new SysUserVo();
sysUserVo.setSortCode( entity.getSortCode() );
sysUserVo.setCreateUser( entity.getCreateUser() );
sysUserVo.setCreateTime( entity.getCreateTime() );
sysUserVo.setUpdateUser( entity.getUpdateUser() );
sysUserVo.setUpdateTime( entity.getUpdateTime() );
sysUserVo.setId( entity.getId() );
sysUserVo.setAccount( entity.getAccount() );
sysUserVo.setNickname( entity.getNickname() );
sysUserVo.setEmail( entity.getEmail() );
sysUserVo.setPhone( entity.getPhone() );
sysUserVo.setAvatar( entity.getAvatar() );
sysUserVo.setGender( entity.getGender() );
sysUserVo.setSecretLevel( entity.getSecretLevel() );
sysUserVo.setStatus( entity.getStatus() );
sysUserVo.setBeforeLoginIp( entity.getBeforeLoginIp() );
sysUserVo.setBeforeLoginAddress( entity.getBeforeLoginAddress() );
sysUserVo.setBeforeLoginTime( entity.getBeforeLoginTime() );
sysUserVo.setBeforeLoginDevice( entity.getBeforeLoginDevice() );
sysUserVo.setNewLoginIp( entity.getNewLoginIp() );
sysUserVo.setNewLoginAddress( entity.getNewLoginAddress() );
sysUserVo.setNewLoginTime( entity.getNewLoginTime() );
sysUserVo.setNewLoginDevice( entity.getNewLoginDevice() );
return sysUserVo;
}
@Override
public SysUserEntity addParamToEntity(SysUserAddParam addParam) {
if ( addParam == null ) {
return null;
}
SysUserEntity sysUserEntity = new SysUserEntity();
sysUserEntity.setAccount( addParam.getAccount() );
sysUserEntity.setNickname( addParam.getNickname() );
sysUserEntity.setEmail( addParam.getEmail() );
sysUserEntity.setPhone( addParam.getPhone() );
sysUserEntity.setAvatar( addParam.getAvatar() );
sysUserEntity.setGender( addParam.getGender() );
sysUserEntity.setSecretLevel( addParam.getSecretLevel() );
sysUserEntity.setStatus( addParam.getStatus() );
return sysUserEntity;
}
@Override
public SysUserEntity editParamToEntity(SysUserEditParam editParam) {
if ( editParam == null ) {
return null;
}
SysUserEntity sysUserEntity = new SysUserEntity();
sysUserEntity.setId( editParam.getId() );
sysUserEntity.setAccount( editParam.getAccount() );
sysUserEntity.setPassword( editParam.getPassword() );
sysUserEntity.setNickname( editParam.getNickname() );
sysUserEntity.setEmail( editParam.getEmail() );
sysUserEntity.setPhone( editParam.getPhone() );
sysUserEntity.setAvatar( editParam.getAvatar() );
sysUserEntity.setGender( editParam.getGender() );
sysUserEntity.setSecretLevel( editParam.getSecretLevel() );
sysUserEntity.setStatus( editParam.getStatus() );
sysUserEntity.setBeforeLoginIp( editParam.getBeforeLoginIp() );
sysUserEntity.setBeforeLoginAddress( editParam.getBeforeLoginAddress() );
sysUserEntity.setBeforeLoginTime( editParam.getBeforeLoginTime() );
sysUserEntity.setBeforeLoginDevice( editParam.getBeforeLoginDevice() );
sysUserEntity.setNewLoginIp( editParam.getNewLoginIp() );
sysUserEntity.setNewLoginAddress( editParam.getNewLoginAddress() );
sysUserEntity.setNewLoginTime( editParam.getNewLoginTime() );
sysUserEntity.setNewLoginDevice( editParam.getNewLoginDevice() );
return sysUserEntity;
}
}
```
# 有关时区的问题
## 时间的存取过程
在java中使用new Date()方式创建亚洲上海时区时间->mysql数据库中使用datetime数据类型存储无时区时间->java读取时间并转为上海时区时间->springmvc中使用jackson将java时间转为字符串(其会按照application配置文件中的配置进行转换)->返回给前端年月日格式的字符串。