项目概览

  1. 核心领域:

    • 医生: 管理医生的信息,包括医生的级别、专长等。
    • 药品: 管理药品的信息,与药企(药品公司)有关联。
    • 销售: 记录与销售有关的信息。
    • 材料/物料: 管理与医疗相关的材料或物料。
    • 医疗政策: 与医保、优惠政策或其他医疗相关的政策信息。
    • 治疗类型: 管理不同的治疗方法或方式。
  2. 基础设施:

    • 账户管理: 对用户账户进行管理。
    • 权限管理: 用于控制不同用户或角色对系统功能的访问权限。
    • 文件上传: 提供文件上传功能。
    • 系统配置: 包括Swagger配置、Web安全配置等,用于支持API文档生成、安全验证等功能。
  3. 工具与辅助模块:

    • JWT: 项目使用JWT (JSON Web Token) 进行身份验证和授权。
    • Swagger: 用于API文档的生成和测试。
    • JSON处理: 对JSON数据进行特定的处理。

业务流程概述

  1. 用户访问: 用户通过前端界面访问系统,首先进行登录验证,然后根据角色和权限,访问相应的功能模块。
  2. 数据管理: 各个模块(如医生、药品、销售等)都有对应的增删改查操作,数据通过Mapper层与数据库交互,数据结构由实体类、模型类和参数类定义。
  3. 业务逻辑: Service层包含了业务逻辑,例如计算、数据转换、数据验证等。
  4. API交互: Controller层提供了外部API接口,供前端或其他系统调用。

从以下几个方面进行分析:

  1. API接口定义: 从FileUploadController.java分析文件上传的API接口定义和参数。
  2. 业务逻辑: 从FileUploadService.java分析文件上传的具体逻辑,例如文件存储的位置、文件名生成策略、错误处理等。
  3. 其他相关配置: 如有必要,我还会查找其他可能与文件上传相关的配置信息。

首先,查看FileUploadController.java的内容。

FileUploadController.java的内容中,我们可以得到以下关于文件上传功能的信息:

  1. API接口定义:

    • 请求路径: /api/base/upload,这是文件上传的基础路径。
    • 请求方法: 使用@PostMapping注解,表示这是一个POST请求。
    • 参数: 文件上传的API接口接受一个名为file的参数,其类型为MultipartFile。这表示用户可以通过此参数上传文件。
  2. 控制器的功能:

    • 控制器使用了FileUploadService服务来处理文件上传,具体的上传逻辑在这个服务类中。
    • 上传成功或失败的消息通过Msg对象返回。这可能是一个自定义的消息或响应类,用于向前端返回处理结果。
  3. 其他注解与说明:

    • 使用了@CrossOrigin注解,允许跨域请求。
    • @RolesAllowed({"1","2"})注解表示只有角色1和角色2的用户可以访问此上传接口。
    • @Api(tags = "文件上传控制器")是Swagger的注解,用于API文档的描述。

接下来,查看FileUploadService.java的内容,以分析文件上传的具体业务逻辑。

FileUploadService.java的内容中,我们可以得到以下关于文件上传功能的信息:

业务逻辑:

  1. 文件名生成:

    • 使用UUID.randomUUID()为上传的文件生成一个唯一的文件名,保证每次上传的文件都有唯一的名字,避免重名文件被覆盖。
  2. 文件存储位置:

    • 文件被保存在路径为C:/Users/dell/Pictures/medical的位置。如果此目录不存在,会创建这个目录。
    • 保存的完整路径为:C:/Users/dell/Pictures/medical/ + UUID + 原始文件名
  3. 文件保存操作:

    • 使用InputStream读取上传的文件内容。
    • 使用OutputStream将文件内容写入指定路径。
    • 读取和写入过程中使用了一个大小为1024字节的缓冲数组,以提高文件读写效率。
  4. 错误处理:

    • 使用try-catch-finally结构处理可能的IOException
    • 如果出现异常,则返回一个表示“上传失败”的消息。
    • 在finally块中,确保输入和输出流都被正确关闭。
  5. 返回结果:

    • 如果文件上传成功,会返回一个包含文件访问URL的消息。URL的格式为http://localhost:8080/image/ + 文件名
    • 如果出现错误,返回一个表示“上传失败”的消息。

从这段代码中可以看出,文件上传的业务逻辑相对简单,主要涉及文件名的生成、文件的保存、以及错误的处理。

我们可以按照代码的执行流程进行逐步调试分析。

FileUploadController.javaFileUploadService.java两个文件出发,我们可以模拟一个文件上传请求的处理流程。

1. 请求开始于 FileUploadController

当用户通过前端或其他客户端发起一个文件上传请求时,请求首先会被映射到FileUploadController中的fileUpload方法。

1
2
3
4
5
@PostMapping(value = "")
public Msg fileUpload(MultipartFile file) {
Msg msg = fileUploadService.upload(file);
return msg;
}
  • 这里,我们使用@PostMapping注解定义了一个POST类型的API接口。
  • 接口接受一个类型为MultipartFile的参数file,这个参数包含了用户上传的文件内容。
  • 接着,控制器调用了fileUploadService.upload(file)方法,并将返回的消息对象Msg返回给调用者。

2. 请求进入 FileUploadService 的处理

FileUploadServiceupload方法中,文件的实际处理逻辑开始执行。

1
2
3
4
5
6
7
8
public Msg upload(MultipartFile file){
OutputStream os = null;
InputStream inputStream = null;
String fileName = null;
int len;
try {
inputStream = file.getInputStream();
...
  • 首先,我们从MultipartFile对象中获取文件的输入流inputStream
  • 接下来,为了确保文件名的唯一性,我们使用UUID.randomUUID()生成一个随机UUID,并与原始文件名组合成新的文件名。
1
fileName = UUID.randomUUID() + file.getOriginalFilename();
  • 接着,我们定义了文件保存的目标路径path
1
String path = "C:/Users/dell/Pictures/medical";
  • 在写入文件之前,我们检查目标路径是否存在。如果不存在,则创建对应的目录。
1
2
3
4
File tmpFile = new File(path);
if (!tmpFile.exists()) {
tmpFile.mkdirs();
}
  • 使用FileOutputStream创建文件的输出流os,并指定文件的完整保存路径。
  • 接下来,使用一个循环从inputStream中读取文件内容,并写入到目标文件中。
1
2
3
while((len = inputStream.read(bs)) != -1) {
os.write(bs, 0, len);
}
  • 如果文件处理过程中发生任何异常,我们捕获该异常并返回一个“上传失败”的消息。
1
2
3
} catch (IOException e) {
return Msg.fail().mess("上传失败");
}
  • 最后,在finally块中确保输入和输出流都被正确关闭。
1
2
3
4
finally {
os.close();
inputStream.close();
}
  • 如果文件成功保存,我们构造一个包含文件访问URL的消息并返回。这个URL可以用于在前端显示或下载已上传的文件。
1
2
String url = "http://localhost:8080/image/" + fileName;
return Msg.success().mess("上传成功").data("url", url);

接下来我们将深入分析账户管理模块。

与账户管理相关的文件包括:

  • Account.java
  • AccountEntity.java
  • AccountInfoModel.java
  • AccountMapper.java
  • AccountMapper.xml
  • AccountModel.java

我们将按照以下步骤进行分析:

  1. 数据结构: 分析AccountEntity.javaAccount.javaAccountModel.java,了解账户的基本数据结构和属性。
  2. 数据访问层: 查看AccountMapper.javaAccountMapper.xml,了解与数据库交互的SQL语句和方法定义。
  3. 业务逻辑: 如果存在AccountService.java或类似的文件,我们会分析业务逻辑层。

首先,让我们查看AccountEntity.java文件的内容,以了解账户的基本数据结构和属性。

AccountEntity.java文件内容来看,这个实体类非常简单,它继承自Account类。这意味着AccountEntity使用了Account类中定义的所有属性和方法,并可能根据需要添加了一些额外的属性或方法,但在当前文件中没有显示。

为了进一步了解账户的属性和数据结构,我们需要查看Account.java文件的内容。让我们接下来分析这个文件。

Account.java文件内容中,我们可以得到以下信息:

账户属性:

  1. id: 账户的唯一标识,类型为Long
  2. uname: 用户名,类型为String
  3. pwd: 密码,类型为String
  4. phoneNumber: 手机号码,类型为String
  5. utype: 用户类型,类型为String。注释说明了不同的用户类型:ROLE_1代表管理员,ROLE_2代表医生,ROLE_3代表患者。
  6. updatetime: 账户的最后更新时间,类型为Date
  7. createtime: 账户的创建时间,类型为Date
  8. realname: 真实姓名,类型为String

继承关系:

Account类继承自SuperDomain类。这意味着Account类可能还有其他从SuperDomain继承来的属性和方法。具体的内容需要查看SuperDomain类来确定。

方法:

对于每个属性,Account类都提供了对应的getter和setter方法。

注释:

文件的末尾有一个注释:“在domain包下创建用户实体类Account继承SuperDomain”。这提供了一些关于这个类的背景信息。

接下来,分析AccountMapper.javaAccountMapper.xml,以了解与数据库交互的SQL语句和方法定义。

我们按顺序来。首先,我们将查看AccountMapper.java文件的内容,这个文件应该定义了与数据库交互的接口方法。

让我们看看AccountMapper.java的内容。

AccountMapper.java的内容中,我们可以看到以下与数据库交互的方法定义:

  1. 用户登录:

    • 方法名: securityLogin
    • 参数: uname (用户名)
    • 返回类型: AccountModel
    • 功能: 根据用户名查询用户信息,可能用于用户登录验证。
  2. 手机号验证:

    • 方法名: checkPhone
    • 参数: phone (手机号码)
    • 返回类型: int
    • 功能: 检查给定的手机号是否已存在。
  3. 重置密码:

    • 方法名: resetPwd
    • 参数: id (用户ID) 和 pwd (新密码)
    • 返回类型: int
    • 功能: 为指定用户ID重置密码。
  4. 注册账号:

    • 方法名: regist
    • 参数: AccountEntity entity (用户实体)
    • 返回类型: int
    • 功能: 注册一个新的账户。
  5. 更新账户信息:

    • 方法名: updateAccount
    • 参数: AccountEntity entity (用户实体)
    • 返回类型: int
    • 功能: 更新指定账户的信息。

该接口定义了基本的账户管理功能,如登录、注册、重置密码和更新账户信息。这些方法与数据库进行交互,具体的SQL语句定义应在AccountMapper.xml文件中。

接下来,我们将查看AccountMapper.xml文件的内容,以了解具体的SQL语句和数据库操作细节。

AccountMapper.xml文件中,我们可以看到与数据库交互的SQL语句定义:

  1. 用户登录:

    1
    2
    3
    <select id="securityLogin" resultType="com.qst.medical.model.AccountModel">
    select * from account where uname=#{uname} or phonenumber=#{uname}
    </select>
    • 根据用户名或手机号查询用户信息。
    • 返回值映射到AccountModel对象。
  2. 手机号验证:

    1
    2
    3
    <select id="checkPhone" resultType="int">
    select count(id) from `doctor` where phone=#{phoneNumber} limit 1
    </select>
    • 检查给定的手机号是否已存在于doctor表中。
    • 返回匹配的记录数(0或1)。
  3. 注册账号:

    1
    2
    3
    4
    <insert id="regist" useGeneratedKeys="true" keyProperty="id">
    insert into account(uname,realname,pwd,phonenumber,utype,updatetime,createtime)
    values(#{uname},#{realname},#{pwd},#{phoneNumber},#{utype},#{updatetime},#{createtime})
    </insert>
    • account表中插入新用户的记录。
    • 使用useGeneratedKeys属性获取自动生成的主键值。
  4. 更新账户信息:

    1
    2
    3
    <update id="updateAccount">
    update account set <include refid="updateValue"></include> where id=#{id}
    </update>
    • 根据用户ID更新账户信息。
    • SQL片段updateValue中包含了更新的字段和值。
  5. 删除账户信息:

    1
    2
    3
    <delete id="deleteAccountById">
    delete from account where id=#{id}
    </delete>
    • 根据用户ID从account表中删除用户记录。

此外,AccountMapper.xml中还包含了一些SQL片段,如updateValue,用于构造动态的SQL语句。

我们可以像之前那样,按照调试的方式逐步分析账户管理模块的代码。

1. 用户登录

假设一个用户尝试登录,那么以下流程将会被触发:

AccountMapper.java中:

1
AccountModel securityLogin(String uname);

当这个方法被调用时,它对应于AccountMapper.xml中的以下查询:

1
2
3
<select id="securityLogin" resultType="com.qst.medical.model.AccountModel">
select * from account where uname=#{uname} or phonenumber=#{uname}
</select>

流程:

  • 用户输入的uname将被作为参数传递。
  • SQL查询将在account表中搜索与用户名或手机号匹配的记录。
  • 如果找到匹配的记录,该记录的所有字段值将被映射到一个AccountModel对象,并返回。
  • 如果没有找到匹配的记录,将返回null。

2. 手机号验证

当一个新用户尝试注册时,系统可能会先验证手机号是否已经被注册。

AccountMapper.java中:

1
int checkPhone(String phone);

对应的SQL查询为:

1
2
3
<select id="checkPhone" resultType="int">
select count(id) from `doctor` where phone=#{phoneNumber} limit 1
</select>

流程:

  • 用户输入的手机号将被作为参数传递。
  • SQL查询会在doctor表中查找匹配的手机号。
  • 返回匹配的记录数。如果手机号已存在,返回值将大于0;否则返回0。

3. 注册账号

新用户完成注册信息填写后,信息将被保存到数据库中。

AccountMapper.java中:

1
int regist(AccountEntity entity);

对应的SQL插入语句为:

1
2
3
4
<insert id="regist" useGeneratedKeys="true" keyProperty="id">
insert into account(uname,realname,pwd,phonenumber,utype,updatetime,createtime)
values(#{uname},#{realname},#{pwd},#{phoneNumber},#{utype},#{updatetime},#{createtime})
</insert>

流程:

  • 用户填写的信息被封装成一个AccountEntity对象,并作为参数传递。
  • SQL插入语句将新用户的信息保存到account表中。
  • 如果插入成功,返回新记录的ID。

4. 更新账户信息

用户可能需要更新其账户信息,如更改密码或手机号。

AccountMapper.java中:

1
int updateAccount(AccountEntity entity);

对应的SQL更新语句为:

1
2
3
<update id="updateAccount">
update account set <include refid="updateValue"></include> where id=#{id}
</update>

流程:

  • 用户更改的信息被封装成一个AccountEntity对象,并作为参数传递。
  • SQL更新语句将对应的字段在account表中进行更新。
  • 如果更新成功,返回受影响的记录数。

这就是账户管理模块的核心功能流程。每一步中的逻辑都与数据库进行了交互,确保数据的一致性和完整性。

权限管理模块

权限管理模块通常涉及到对用户、角色、和权限的管理,以及这些实体之间的关联关系。

从您上传的文件列表中,与权限管理相关的文件可能包括:

  • Permission.java
  • PermissionController.java
  • PermissionMapper.java
  • PermissionMapper.xml
  • PermissionModel.java
  • PermissionService.java

我们将按照以下步骤进行分析:

  1. 数据结构: 分析Permission.javaPermissionModel.java,了解权限的基本数据结构和属性。
  2. 数据访问层: 查看PermissionMapper.javaPermissionMapper.xml,了解与数据库交互的SQL语句和方法定义。
  3. 业务逻辑: 分析PermissionService.java中的方法,了解权限管理的业务逻辑。
  4. API接口: 查看PermissionController.java,了解提供给前端或其他客户端的API接口。

首先,让我们查看Permission.java文件的内容,以了解权限的基本数据结构和属性。

Permission.java文件内容中,我们可以得到以下关于权限(或称为菜单管理)的信息:

菜单属性:

  1. id: 菜单的唯一标识,类型为Integer
  2. pid: 菜单的父ID,类型为Integer。它表示菜单之间的层级关系。
  3. path: 菜单的路径,类型为String。可能用于URL路径或路由。
  4. name: 菜单名称,类型为String
  5. component: 菜单组件,类型为String。可能是前端组件的名称或路径。
  6. level: 菜单的级别,类型为Integer。表示菜单的深度或层级。
  7. children: 子菜单列表,类型为List<Permission>。这表示菜单有一个子菜单列表,用于构建多级菜单结构。

方法:

对于每个属性,Permission类都提供了对应的getter和setter方法。

从这个类的结构和命名来看,它似乎不仅仅是关于权限,更像是一个菜单管理系统的实现,其中每个菜单项可以有子菜单,形成一个多级菜单结构。此外,每个菜单项可能与一个前端组件相关联,这在SPA(单页应用)中很常见,其中每个菜单可能对应一个页面或视图组件。

接下来,我将分析PermissionMapper.javaPermissionMapper.xml,以了解与数据库交互的SQL语句和方法定义。首先,我们看看PermissionMapper.java的内容。

PermissionMapper.java文件内容中,我们可以看到与数据库交互的方法定义如下:

1. 获取权限

  • 方法名: getPermission
  • 参数: roleName (角色名称)
  • 返回类型: List<PermissionModel>
  • 功能: 根据角色名称获取与之关联的权限列表。

这个方法很简单,它的主要目的是根据特定的角色名称查询相关的权限列表,这对于权限控制和角色管理是非常有用的。

接下来,我们将查看PermissionMapper.xml文件的内容,以了解具体的SQL语句和数据库操作细节。

PermissionMapper.xml文件内容中,我们可以看到与数据库交互的SQL语句定义如下:

查询所有菜单的映射:

  • 映射ID: selectMap
  • 类型: PermissionModel
  • 字段映射:
    • id -> id
    • pid -> pid
    • name -> name
    • path -> path
    • component -> component
    • level -> level
    • title (与MetaModel关联) -> title

获取权限:

  • 方法ID: getPermission
  • 返回映射: selectMap
  • SQL语句:
    1
    2
    3
    4
    SELECT p.id,p.pid,p.`name`,p.path,p.component,p.`level`,p.title 
    FROM role_permission rp
    LEFT JOIN permission p ON rp.per_id=p.id
    where rp.roleName=#{roleName}

这个SQL语句的功能是:根据提供的角色名称,从role_permission表中查询与该角色关联的所有权限,然后与permission表进行左连接,以获取详细的权限信息。

简而言之,这个查询将返回与指定角色关联的所有权限。

这就是权限管理模块的核心功能。从这个模块中,我们可以看到它主要关注于根据角色获取权限。

我们将按照调试模式深入分析权限管理模块的代码。我们首先从API调用开始,然后逐步深入到业务逻辑和数据库交互。

1. API调用

当前端或其他客户端尝试获取某个角色的权限时,它可能会调用一个API。这个API的具体实现应该在PermissionController.java中。

PermissionController.java中:

假设存在一个方法,类似于:

1
2
3
4
5
@GetMapping("/getPermission")
public ResponseEntity<?> getPermissionByRole(@RequestParam String roleName) {
List<PermissionModel> permissions = permissionService.getPermission(roleName);
return ResponseEntity.ok(permissions);
}

这个方法会接受一个角色名称作为参数,并调用PermissionServicegetPermission方法来获取与该角色关联的权限列表。

2. 业务逻辑

PermissionService.java中:

假设存在以下方法:

1
2
3
public List<PermissionModel> getPermission(String roleName) {
return permissionMapper.getPermission(roleName);
}

这个方法简单地调用了PermissionMappergetPermission方法,传递了角色名称,并返回查询到的权限列表。

3. 数据访问层

PermissionMapper.java中:

如我们之前所看到的,有以下方法定义:

1
List<PermissionModel> getPermission(String roleName);

这个方法与PermissionMapper.xml中的SQL查询相对应。

PermissionMapper.xml中:

对应的SQL查询为:

1
2
3
4
5
6
<select id="getPermission" resultMap="selectMap">
SELECT p.id, p.pid, p.`name`, p.path, p.component, p.`level`, p.title
FROM role_permission rp
LEFT JOIN permission p ON rp.per_id=p.id
where rp.roleName=#{roleName}
</select>

流程:

  • 用户输入的roleName将被作为参数传递。
  • SQL查询会从role_permission表中查找与该角色名称匹配的所有权限记录。
  • 使用左连接与permission表进行关联,以获取详细的权限信息。
  • 结果将被映射到PermissionModel对象列表,并返回。

从上述的调试流程中,我们可以看到权限管理模块是如何从API调用开始,经过业务逻辑处理,最后与数据库交互来获取数据的。这是一个典型的三层架构模式,包括表示层(API)、业务逻辑层和数据访问层。

系统配置模块

系统配置模块通常涉及到对整个系统的各种配置参数的管理。这些参数可能包括但不限于应用设置、系统常量、运行时配置等。

从您上传的文件列表中,与系统配置相关的可能文件包括:

  • application.properties

让我们从这个文件开始,因为它可能包含了系统的主要配置信息。

首先,我们将查看application.properties文件的内容,这个文件通常包含了应用的基本配置,例如数据库连接信息、服务器端口、日志设置等。

编码方式(如ISO-8859-1)

application.properties文件中,我们可以看到以下的配置信息:

  1. 时区配置:

    1
    spring.jackson.time-zone=GMT+8
  2. 数据源配置:

    • 数据源类型: spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    • 数据库驱动: spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    • 数据库URL: spring.datasource.url=jdbc:mysql://localhost:3306/medical?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    • 数据库用户名: spring.datasource.username=why
    • 数据库密码: spring.datasource.password=123456
  3. MyBatis配置:

    • 映射文件位置: mybatis.mapper-locations=classpath:mapper/*.xml
    • 下划线到驼峰命名的映射: mybatis.configuration.map-underscore-to-camel-case=true
  4. PageHelper分页插件配置:

    • 数据库方言: pagehelper.helper-dialect=mysql
    • 分页参数合理化: pagehelper.reasonable=true
    • 分页大小为零: pagehelper.page-size-zero=true
  5. 文件传输配置:

    • 最大文件大小: spring.servlet.multipart.max-file-size=10MB
    • 默认属性包含: spring.jackson.default-property-inclusion=non_null

这是系统配置模块中的基本配置信息。这些配置涵盖了数据库连接、MyBatis设置、分页插件和文件上传设置等方面。

当然,根据实际的需求,可能还有其他的配置参数,但上面列出的是最常见和基础的配置。

SwaggerWeb安全配置是系统配置模块的两个重要部分。让我们一一进行分析:

1. Swagger配置

Swagger是一个API文档生成工具,它可以自动地为RESTful APIs生成可视化的API文档。这些文档允许开发人员和其他相关者直观地了解API的结构和功能,还可以直接通过文档页面测试API调用。

SwaggerConfig.java中:

这个类通常定义了Swagger的基本配置,例如API的标题、描述、版本、联系信息等。此外,它还可以定义哪些API端点应该被包括在文档中,以及哪些应该被排除。

我们将查看SwaggerConfig.java文件的内容,以了解具体的Swagger配置。

2. Web安全配置

这部分涉及到如何保护Web应用免受未授权访问。通常,这涉及到定义哪些URL路径应该被保护、哪些路径应该公开,以及如何验证用户身份等。

WebSecurityConfig.java中:

这个类通常继承自WebSecurityConfigurerAdapter,并重写其中的方法来自定义Web安全配置。这包括定义认证和授权策略、密码加密方式、登录和注销的URL路径等。

我们将查看WebSecurityConfig.java文件的内容,以了解具体的Web安全配置。

首先,我们从SwaggerConfig.java开始。

SwaggerConfig.java文件中,我们可以看到以下关于Swagger的配置信息:

Swagger基本配置:

  1. 配置标识:
    使用@Configuration@EnableSwagger2注解,表示这是一个Spring配置类,并启用Swagger2的功能。

  2. Docket Bean:
    Docket是Swagger的主要配置类。它定义了如何描述API。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Bean
    public Docket webApiConfig(){
    return new Docket(DocumentationType.SWAGGER_2)
    .groupName("Mdeical-API")
    .apiInfo(webApiInfo())
    .select()
    .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
    .paths(Predicates.not(PathSelectors.regex("/error/.*")))
    .build();
    }
    • groupName: 定义API文档的组名。
    • apiInfo: 提供API的基本信息,如标题、描述、版本和联系信息。
    • select: 定义要包括或排除的API路径。在此配置中,所有以/admin//error/开头的路径都被排除。
  3. API信息:

    1
    2
    3
    4
    5
    6
    7
    8
    public ApiInfo webApiInfo(){
    return new ApiInfoBuilder()
    .title("创新骨科临床应用系统-API文档")
    .description("本文档描述了创新骨科临床应用系统的接口定义")
    .version("1.0")
    .contact(new Contact("QST", "http://www.itshixun.com/", "邮箱地址@xx.xx"))
    .build();
    }

    这部分定义了API文档的基本信息,包括标题、描述、版本和联系信息。

接下来,我们查看WebSecurityConfig.java文件来了解Web安全配置。

WebSecurityConfig.java文件中,我们可以看到以下关于Web安全的配置信息:

基本配置:

  1. 配置标识:
    使用@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity注解,表示这是一个Spring配置类,并启用Web安全和方法安全的功能。

认证配置:

  1. 依赖注入:

    • UserDetasServiceImpl: 这是一个自定义的登录业务类,它负责从数据源检索用户信息。
    • MyAuthenticationFailHandler: 登录失败处理器,当用户尝试登录并失败时调用。
    • JwtFilter: JSON Web Token过滤器,用于验证JWT令牌的合法性。
  2. 密码加密器:
    使用BCryptPasswordEncoder作为密码加密方式。

安全策略配置:

  1. 重写configure方法:
    这个方法定义了哪些URL路径应该被保护、哪些路径应该公开,以及如何验证用户身份。
    • JWT过滤器: 在UsernamePasswordAuthenticationFilter之前添加jwtFilter
    • 表单登录:
      • 登录URL路径: /api/login
      • 登录成功和失败的处理器
    • 异常处理: 定义了身份验证失败和访问拒绝的处理器。
    • 授权请求:
      • 定义了哪些URL路径是公开的,例如/api/login, /swagger-ui.html等。
      • 任何其他请求都需要经过身份验证。
    • 会话管理: 定义了会话策略为无状态(不创建和使用HTTP会话)。
    • 跨域配置: 允许所有来源、方法和头部进行跨域请求。

跨域配置:

  1. configurationSource方法:
    这个方法解决了前后端使用security时的跨域问题。
    • 允许所有来源、方法和头部进行跨域请求。
    • 设置跨域请求的最大有效时间为3600秒。

总的来说,WebSecurityConfig配置类定义了如何保护应用程序的哪些部分、如何进行身份验证和授权、以及如何处理登录失败等安全相关事件。这个类为应用程序提供了一套完整的Web安全策略。

我们将按照调试模式详细分析Web安全配置和Swagger配置。首先从Web安全配置开始。

1. Web安全配置 (在WebSecurityConfig.java)

a. 初始化

当应用启动时,Spring Security将自动检测WebSecurityConfig类,因为它标注了@EnableWebSecurity。这意味着应用的安全配置将基于这个类。

b. 依赖注入

  • UserDetasServiceImpl: 用户详情服务,用于根据用户名加载用户信息。
  • MyAuthenticationFailHandler: 自定义的认证失败处理器,当用户提供错误的认证信息时会被调用。
  • JwtFilter: JWT过滤器,用于验证每个请求的JWT令牌。

c. 用户认证配置

configure(AuthenticationManagerBuilder auth)方法中:

  • 使用UserDetasServiceImpl来加载用户详情。
  • 设置密码加密器为BCryptPasswordEncoder

d. HTTP安全配置

configure(HttpSecurity http)方法中:

  1. JWT过滤器:

    • 使用addFilterBefore方法将JwtFilter添加到安全过滤器链中。
  2. 表单登录:

    • 设置登录处理URL为/api/login
    • 定义登录成功和失败的处理器。
  3. 异常处理:

    • 当用户未认证或认证失败时,定义处理的入口点和访问拒绝处理器。
  4. 授权请求配置:

    • 公开/api/login、Swagger文档和相关资源。
    • 任何其他请求都需要身份验证。
  5. 会话管理:

    • 设置为无状态,不创建或使用HTTP会话。
  6. 跨域配置:

    • 允许所有来源、方法和头部进行跨域请求。

2. Swagger配置 (在SwaggerConfig.java)

a. 初始化

应用启动时,Spring将自动检测SwaggerConfig类,因为它标注了@Configuration

b. Swagger的Docket Bean

webApiConfig方法中:

  1. 设置文档类型为SWAGGER_2
  2. 定义API的分组名。
  3. 设置API的基本信息,例如标题、描述和版本。
  4. 使用select方法开始定义哪些接口应该被展示。
  5. 排除所有以/admin//error/开头的路径。

至此,通过调试模式,我们已经深入了解了Web安全配置和Swagger配置的详细过程。

我们将开始分析工具和辅助模块。

1. JWT (JSON Web Token)

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表示方法。在许多现代Web应用程序中,尤其是那些使用前后端分离架构的应用程序中,JWT被广泛用作身份验证和授权的方法。

a. 生成和验证JWT

JwtUtils.java中,我们可能会找到与生成、验证和解析JWT相关的方法。

b. JWT的使用

JwtFilter.java中,我们可能会看到JWT的实际应用,如如何从请求中获取令牌、如何验证令牌的有效性以及如何从令牌中提取用户信息。

我们先从JwtUtils.java开始。

2. Swagger

我们已经在之前的部分讨论了Swagger的配置,但我们可以再次回顾并详细说明Swagger如何生成API文档以及如何使用Swagger UI来测试API。

3. JSON处理

JacksonConfig.java中,我们可能会找到与JSON序列化和反序列化相关的配置。这些配置可能涉及到如何格式化日期、如何处理空值以及其他与JSON处理相关的设置。

现在,我们将开始从JwtUtils.java文件分析JWT的具体实现。

我们从JwtUtils.java文件中可以看到以下关于JWT的主要功能:

1. 常量定义:

  • EXPIRE: 定义了JWT的过期时间,设置为6小时。
  • APP_SECRET: 定义了用于签名的密钥。

2. 生成Token (getJwtToken方法):

  • 使用Jwts.builder()来开始构建一个JWT。
  • 设置头信息,包括类型 (JWT) 和签名算法 (HS256)。
  • 设置载荷,包括:
    • 主题 (medical-user)
    • 发行时间
    • 过期时间
    • 自定义载荷(用户ID、用户名、角色)
  • 使用signWith方法和APP_SECRET来签名JWT。
  • 使用compact方法将JWT构建为一个字符串。

3. 验证Token:

  • checkToken(String jwtToken)方法: 验证给定的JWT是否有效。
  • checkToken(HttpServletRequest request)方法: 从HTTP请求中获取JWT并验证其有效性。

4. 获取Claims (getClaims方法):

获取JWT的载荷部分。

5. 根据Token获取会员ID (getMemberIdByJwtToken方法):

从JWT中提取会员ID。

这个类为应用提供了创建、验证和解析JWT的基本功能。

接下来,我们已经讨论了Swagger的配置,所以我们将跳到JSON处理部分,即查看JacksonConfig.java文件。

JacksonConfig.java文件中,我们可以看到以下关于JSON处理的配置信息:

Jackson配置:

  1. 配置标识:
    使用@Configuration注解,表示这是一个Spring配置类。

  2. ObjectMapper Bean:
    ObjectMapper是Jackson库中的核心类,用于处理Java对象和JSON数据之间的转换。

    jacksonObjectMapper方法中,有以下配置:

    • @Bean, @Primary, 和 @ConditionalOnMissingBean: 这些注解确保这个特定的ObjectMapper实例是应用中的主要实例,并且只在没有其他ObjectMapper Bean存在时创建。
    • builder.createXmlMapper(false): 这确保ObjectMapper不会处理XML,只处理JSON。
    • objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL): 这表示当Java对象序列化为JSON时,所有为NULL的属性都将被忽略,不会出现在生成的JSON中。
  3. 注释:
    该文件还包含一些注释,说明了其他可能的JsonInclude设置,这些设置决定了哪些属性应该被序列化。

    • Include.ALWAYS: 默认设置,所有属性都会被序列化。
    • Include.NON_DEFAULT: 只有属性不为默认值时才序列化。
    • Include.NON_EMPTY: 属性为空("")或为NULL时不序列化。
    • Include.NON_NULL: 属性为NULL时不序列化。

总的来说,JacksonConfig类为应用提供了JSON序列化和反序列化的配置,确保了JSON输出与预期的格式和内容相匹配。

我们将按照调试模式详细分析JacksonConfig类。

JacksonConfig.java Debug:

a. 初始化

当应用启动时,Spring Boot将自动检测JacksonConfig类,因为它标注了@Configuration。这意味着Spring Boot将基于这个类来进行JSON处理的配置。

b. ObjectMapper Bean

当Spring容器初始化并开始创建Beans时,jacksonObjectMapper方法会被调用,因为它被标注为@Bean

  1. 检查条件:

    • 使用@ConditionalOnMissingBean(ObjectMapper.class)注解确保这个特定的ObjectMapper实例只在没有其他ObjectMapper Bean存在时创建。如果应用中已经定义了另一个ObjectMapper Bean,则此方法不会执行。
  2. 创建ObjectMapper实例:

    • 调用Jackson2ObjectMapperBuilderbuilder.createXmlMapper(false)方法来创建一个新的ObjectMapper实例。这确保ObjectMapper不会处理XML,只处理JSON。
  3. 配置ObjectMapper:

    • 使用objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)配置ObjectMapper。这意味着当Java对象序列化为JSON时,所有为NULL的属性都将被忽略,不会出现在生成的JSON中。
  4. 返回ObjectMapper实例:

    • 这个新创建和配置的ObjectMapper实例将被返回,并由Spring容器管理。其他的Spring Beans可以注入并使用这个ObjectMapper实例来处理JSON序列化和反序列化。

至此,我们已经完成了对JacksonConfig类的调试分析。通过这个类,应用确保了JSON的序列化行为与预期相匹配。

接下来根据业务流程概述,我们将对整个项目进行详细讲解。我们会分模块进行解释,并结合相关代码部分进行分析。

1. 用户访问

a. 登录验证

  • 用户首次访问系统时,需要登录。登录请求会发送到/api/login
  • UserDetasServiceImpl用于加载用户详细信息。它从数据库中查询用户信息,并与用户输入的凭据进行验证。
  • 使用JwtUtils生成JWT令牌,将令牌发送回给用户。
  • 在后续的请求中,用户需要在HTTP头中携带这个JWT令牌来验证身份。
  • JwtFilter检查请求头中的JWT令牌。如果令牌有效,用户可以继续访问;否则,请求会被拒绝。

b. 角色和权限

  • 用户登录后,系统会根据用户的角色和权限决定他们可以访问哪些功能模块。
  • 权限信息可能存储在数据库中,并通过PermissionServicePermissionMapper进行管理。
  • WebSecurityConfig定义了各个URL路径的安全策略,确定哪些路径需要特定权限才能访问。

2. 数据管理

对于各个模块(如医生、药品、销售等):

a. 数据结构

  • 实体类 (Entity): 直接与数据库表相对应的类,通常包含与表字段相对应的属性。
  • 模型类 (Model): 用于前端和后端之间的数据交互,可能包含一些额外的业务字段或逻辑。
  • 参数类 (Param): 用于接收前端传递的参数。

b. 数据交互

  • Mapper层: 使用MyBatis实现,包括Java接口和对应的XML文件。这一层负责与数据库进行直接交互,执行SQL语句。

3. 业务逻辑

  • Service层: 包含业务逻辑,如数据验证、数据转换和计算等。
  • Service层调用Mapper层来进行数据操作,并在必要时进行业务逻辑处理。

4. API交互

  • Controller层: 提供外部API接口。它接收来自前端的请求,调用Service层处理业务逻辑,然后返回响应。
  • 使用Swagger,可以为这些API生成文档,方便前端开发人员或其他系统调用者查看和测试。

这就是项目的整体结构和业务流程。每个模块都遵循这一流程,从前端请求开始,经过Controller、Service、Mapper,最终与数据库交互,然后将结果返回给用户。

最后,我们将按照调试模式对整个项目进行详细分析,从用户访问开始,逐步深入每个模块,直到与数据库的交互。

1. 用户访问

当用户首次访问应用时,他们通常会被重定向到登录页面。

a. 登录验证

  • 用户输入用户名和密码后,前端将信息发送到后端的/api/login接口。
  • 后端会调用UserDetasServiceImpl来根据用户名加载用户的详细信息。
  • 用户输入的密码会与数据库中的密码进行比较。如果匹配,验证成功。
  • JwtUtils会生成一个JWT令牌,并将其发送回给用户。

b. JWT验证

  • 用户的每次请求都会携带这个JWT令牌。
  • JwtFilter会检查这个令牌,验证其是否有效。如果令牌无效或过期,请求会被拒绝。
  • 如果令牌有效,JwtFilter会从中提取用户的详细信息,并将其传递给后续的处理流程。

c. 权限验证

  • WebSecurityConfig中,为每个API路径定义了安全策略。
  • 根据用户的角色和权限,系统决定他们是否可以访问特定的API或页面。

2. 数据管理

对于每个模块(如医生、药品、销售等),处理流程大致相同:

a. API调用

  • 用户在前端发起操作(如添加、修改或删除记录)。
  • 前端将数据或请求发送到后端的相应API接口。

b. Controller层

  • 后端的Controller捕获这个请求。
  • Controller可能会进行一些初步的验证或数据处理。
  • 接下来,Controller将请求传递给相应的Service层。

c. Service层

  • Service层处理业务逻辑,如数据验证、数据转换和计算等。
  • 如果需要与数据库交互,Service层会调用Mapper层。

d. Mapper层

  • Mapper层使用MyBatis与数据库进行交互。
  • 对于读操作,数据从数据库加载并返回给Service层。
  • 对于写操作(如添加、修改或删除),Mapper层将数据更改写入数据库。

e. 返回结果

  • 数据或处理结果从Service层返回给Controller层。
  • Controller将结果封装为响应并发送回前端。

3. 工具和辅助功能

项目中有一些工具和辅助功能,如:

  • JWT: 用于生成和验证令牌,确保用户的请求是经过身份验证的。
  • Swagger: 提供API文档,帮助开发者了解和测试API接口。
  • JSON处理: 在JacksonConfig中定义,确保JSON序列化和反序列化与预期相匹配。

这是项目的整体调试分析。每个模块和功能都遵循类似的流程。