This commit is contained in:
zzy
2026-05-24 00:24:56 +08:00
commit 29eb268180
131 changed files with 25500 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan("com.example.mapper")
@EnableScheduling
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}

View File

@@ -0,0 +1,9 @@
package com.example.common;
public interface Constants {
String TOKEN = "token";
String USER_DEFAULT_PASSWORD = "123456";
}

View File

@@ -0,0 +1,72 @@
package com.example.common;
public class Result {
private String code;
private String msg;
private Object data;
private Result(Object data) {
this.data = data;
}
public Result() {
}
public static Result success() {
Result result = new Result();
result.setCode("200");
result.setMsg("请求成功");
return result;
}
public static Result success(Object data) {
Result result = success();
result.setData(data);
return result;
}
public static Result error() {
Result result = new Result();
result.setCode("500");
result.setMsg("请求失败");
return result;
}
public static Result error(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
public static Result error(String msg) {
Result result = new Result();
result.setCode("500");
result.setMsg(msg);
return result;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}

View File

@@ -0,0 +1,25 @@
package com.example.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域配置
*/
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}

View File

@@ -0,0 +1,82 @@
package com.example.common.config;
import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.*;
import com.example.exception.CustomException;
import com.example.service.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* jwt拦截器
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(JwtInterceptor.class);
@Resource
private AdminService adminService;
@Resource
private DoctorService doctorService;
@Resource
private PatientService patientService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 1. 从http请求的header中获取token
String token = request.getHeader(Constants.TOKEN);
if (ObjectUtil.isEmpty(token)) {
// 如果没拿到,从参数里再拿一次
token = request.getParameter(Constants.TOKEN);
}
// 2. 开始执行认证
if (ObjectUtil.isEmpty(token)) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
Account account = null;
try {
// 解析token获取存储的数据
String userRole = JWT.decode(token).getAudience().get(0);
String userId = userRole.split("-")[0];
String role = userRole.split("-")[1];
// 根据userId查询数据库
if ("admin".equals(role)) {
account = adminService.selectById(Integer.valueOf(userId));
}
if ("doctor".equals(role)) {
account = doctorService.selectById(Integer.valueOf(userId));
}
if ("patient".equals(role)) {
account = patientService.selectById(Integer.valueOf(userId));
}
} catch (Exception e) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
if (ObjectUtil.isNull(account)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
try {
// 用户密码加签验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(account.getPassword())).build();
jwtVerifier.verify(token); // 验证token
} catch (JWTVerificationException e) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
return true;
}
}

View File

@@ -0,0 +1,88 @@
package com.example.common.config;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.entity.Account;
import com.example.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
@Component
public class TokenUtils {
private static AdminService staticAdminService;
private static DoctorService staticDoctorService;
private static PatientService staticPatientService;
private static final Logger log = LoggerFactory.getLogger(TokenUtils.class);
@Resource
private AdminService adminService;
@Resource
private DoctorService doctorService;
@Resource
private PatientService patientService;
@PostConstruct
public void setUserService() {
staticAdminService = adminService;
staticDoctorService = doctorService;
staticPatientService = patientService;
}
/**
* 生成token
*/
public static String genToken(String userRole, String password) {
return JWT.create().withAudience(userRole) // 将 userId-role 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期
.sign(Algorithm.HMAC256(password)); // 以 password 作为 token 的密钥
}
/**
* 获取当前登录的用户信息
*/
public static Account getCurrentUser() {
String token = null;
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
token = request.getParameter("token");
}
if (StrUtil.isBlank(token)) {
log.error("获取当前登录的token失败 token: {}", token);
return null;
}
// 解析token获取用户的id
String userRole = JWT.decode(token).getAudience().get(0);
String userId = userRole.split("-")[0];
String role = userRole.split("-")[1];
Account account = null;
if ("admin".equals(role)) {
account = staticAdminService.selectById(Integer.valueOf(userId));
}
if ("doctor".equals(role)) {
account = staticDoctorService.selectById(Integer.valueOf(userId));
}
if ("patient".equals(role)) {
account = staticPatientService.selectById(Integer.valueOf(userId));
}
return account;
} catch (Exception e) {
log.error("获取当前登录的管理员信息失败, token={}", token, e);
return null;
}
}
}

View File

@@ -0,0 +1,24 @@
package com.example.common.config;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private JwtInterceptor jwtInterceptor;
// 加自定义拦截器JwtInterceptor设置拦截规则
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor).addPathPatterns("/**")
.excludePathPatterns("/")
.excludePathPatterns("/login", "/register", "/files/**", "/role/selectAll","/institution/selectAll")
.excludePathPatterns("/doctor") // 放行医生列表获取接口,用于患者注册时选择医生
.excludePathPatterns("/ws/**", "/notification/**") // 放行WebSocket和通知相关的路径
.excludePathPatterns("/eventInstitution/checkAndUpdateEventStatus"); // 放行事件状态检查接口
}
}

View File

@@ -0,0 +1,27 @@
package com.example.common.enums;
public enum ResultCodeEnum {
SUCCESS("200", "成功"),
PARAM_ERROR("400", "参数异常"),
TOKEN_INVALID_ERROR("401", "无效的token"),
TOKEN_CHECK_ERROR("401", "token验证失败请重新登录"),
PARAM_LOST_ERROR("4001", "参数缺失"),
NO_AUTH_ERROR("403", "没有操作权限"),
SYSTEM_ERROR("500", "系统异常"),
USER_EXIST_ERROR("5001", "用户名已存在"),
USER_NOT_LOGIN("5002", "用户未登录"),
USER_ACCOUNT_ERROR("5003", "账号或密码错误"),
USER_NOT_EXIST_ERROR("5004", "用户不存在"),
PARAM_PASSWORD_ERROR("5005", "原密码输入错误"),
;
public String code;
public String msg;
ResultCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
}

View File

@@ -0,0 +1,14 @@
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,20 @@
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类
*/
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter自动注册使用了@ServerEndpoint注解的bean
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@@ -0,0 +1,56 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.service.AIChatService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* AI聊天控制器
*/
@RestController
@RequestMapping("/aiChat")
public class AIChatController {
@Resource
private AIChatService aiChatService;
/**
* 发送消息到AI并获取回复
*/
@PostMapping("/sendMessage")
public Result sendMessage(@RequestBody Map<String, String> params) {
String message = params.get("message");
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "请先登录");
}
// 获取AI回复
String aiReply = aiChatService.sendMessage(message);
Map<String, Object> userMessage = new HashMap<>();
userMessage.put("content", message);
userMessage.put("messageType", "user");
userMessage.put("createTime", LocalDateTime.now());
Map<String, Object> aiMessage = new HashMap<>();
aiMessage.put("content", aiReply);
aiMessage.put("messageType", "ai");
aiMessage.put("createTime", LocalDateTime.now());
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("userMessage", userMessage);
resultMap.put("aiMessage", aiMessage);
return Result.success(resultMap);
}
}

View File

@@ -0,0 +1,48 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.Patient;
import com.example.service.PatientService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 账号相关接口
*/
@RestController
@RequestMapping("/account")
public class AccountController {
@Resource
private PatientService patientService;
/**
* 获取所有患者列表(仅管理员可用)
*
* @param name 可选的患者姓名过滤
* @return 患者列表
*/
@GetMapping("/listPatients")
public Result listPatients(@RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"admin".equals(currentUser.getRole())) {
return Result.error("401", "无权限,只有管理员可以访问此接口");
}
Patient searchParam = null;
if (name != null && !name.isEmpty()) {
searchParam = new Patient();
searchParam.setName(name);
}
List<Patient> patients = patientService.selectAll(searchParam);
return Result.success(patients);
}
}

View File

@@ -0,0 +1,93 @@
package com.example.controller;
import com.example.common.Result;
import com.example.entity.Admin;
import com.example.service.AdminService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 描述:管理员相关接口
*/
@RestController
@RequestMapping("/admin")
public class AdminController {
@Resource
AdminService adminService;
/**
* 新增
*/
@PostMapping("/add")
public Result add(@RequestBody Admin admin) {
adminService.add(admin);
return Result.success();
}
/**
* 删除
*/
@DeleteMapping("/delete/{id}")
public Result delete(@PathVariable Integer id) {
adminService.deleteById(id);
return Result.success();
}
/**
* 批量删除
*/
@DeleteMapping("/delete/batch")
public Result delete(@RequestBody List<Integer> ids) {
adminService.deleteBatch(ids);
return Result.success();
}
/**
* 更新
*/
@PutMapping("/update")
public Result update(@RequestBody Admin admin) {
adminService.updateById(admin);
return Result.success();
}
/**
* 查询单个
*/
@GetMapping("/selectById/{id}")
public Result selectById(@PathVariable Integer id) {
Admin admin = adminService.selectById(id);
return Result.success(admin);
}
/**
* 查询所有
*/
@GetMapping("/selectAll")
public Result selectAll(Admin admin) {
List<Admin> list = adminService.selectAll(admin);
return Result.success(list);
}
/**
* 查询所有
*/
@GetMapping("/selectPage")
public Result selectPage(
Admin admin,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageInfo<Admin> pageInfo = adminService.selectPage(admin, pageNum, pageSize);
return Result.success(pageInfo);
}
}

View File

@@ -0,0 +1,109 @@
package com.example.controller;
import com.example.common.Result;
import com.example.entity.Announcement;
import com.example.service.AnnouncementService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 公告控制器
*/
@RestController
@RequestMapping("/announcement")
public class AnnouncementController {
@Resource
private AnnouncementService announcementService;
/**
* 添加公告
* @param announcement 公告信息
* @return 结果
*/
@PostMapping
public Result add(@RequestBody Announcement announcement) {
announcementService.add(announcement);
return Result.success();
}
/**
* 更新公告
* @param announcement 公告信息
* @return 结果
*/
@PutMapping
public Result update(@RequestBody Announcement announcement) {
announcementService.update(announcement);
return Result.success();
}
/**
* 删除公告
* @param id 公告ID
* @return 结果
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
announcementService.delete(id);
return Result.success();
}
/**
* 获取公告详情
* @param id 公告ID
* @return 公告信息
*/
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Announcement announcement = announcementService.getById(id);
return Result.success(announcement);
}
/**
* 获取所有已发布的公告(分页,所有用户可访问)
* @param pageNum 页码
* @param pageSize 每页大小
* @param title 标题关键字
* @return 分页公告列表
*/
@GetMapping("/page")
public Result getPublishedPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String title) {
PageInfo<Announcement> pageInfo = announcementService.getPublishedPage(pageNum, pageSize, title);
return Result.success(pageInfo);
}
/**
* 获取所有公告(分页,包括草稿,仅管理员可用)
* @param pageNum 页码
* @param pageSize 每页大小
* @param title 标题关键字
* @return 分页公告列表
*/
@GetMapping("/adminPage")
public Result getAllForAdmin(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String title) {
PageInfo<Announcement> pageInfo = announcementService.getAllForAdmin(pageNum, pageSize, title);
return Result.success(pageInfo);
}
/**
* 获取最新的几条已发布公告(用于首页展示)
* @param limit 限制条数
* @return 公告列表
*/
@GetMapping("/latest")
public Result getLatestPublished(
@RequestParam(defaultValue = "5") Integer limit) {
List<Announcement> list = announcementService.getLatestPublished(limit);
return Result.success(list);
}
}

View File

@@ -0,0 +1,116 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.AssessmentReport;
import com.example.service.AssessmentReportService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 评估报告控制器
*/
@RestController
@RequestMapping("/assessmentReport")
public class AssessmentReportController {
@Resource
private AssessmentReportService assessmentReportService;
/**
* 手动生成评估报告
*/
@PostMapping("/generate")
public Result generate(@RequestBody Map<String, Object> params) {
Integer patientId = params.get("patientId") != null ? Integer.valueOf(params.get("patientId").toString()) : null;
String reportPeriod = params.get("reportPeriod") != null ? params.get("reportPeriod").toString() : null;
if (patientId == null) {
return Result.error("400", "缺少必要参数patientId");
}
if (reportPeriod == null) {
return Result.error("400", "缺少必要参数reportPeriod");
}
AssessmentReport report = assessmentReportService.generateReport(patientId, reportPeriod);
return Result.success(report);
}
/**
* 获取患者的评估报告列表
*/
@GetMapping("/patient/{patientId}")
public Result getReportsByPatientId(@PathVariable Integer patientId) {
List<AssessmentReport> reports = assessmentReportService.getReportsByPatientId(patientId);
return Result.success(reports);
}
/**
* 获取医生负责的患者的评估报告列表
*/
@GetMapping("/doctor")
public Result getReportsByDoctor() {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "未登录");
}
List<AssessmentReport> reports = assessmentReportService.getReportsByDoctorId(currentUser.getId());
return Result.success(reports);
}
/**
* 获取所有评估报告列表(管理员使用)
*/
@GetMapping("/admin")
public Result getAllReports() {
List<AssessmentReport> reports = assessmentReportService.getAllReports();
return Result.success(reports);
}
/**
* 添加医生批注
*/
@PutMapping("/comment/{reportId}")
public Result addComment(@PathVariable Integer reportId, @RequestParam(required = false) String comment) {
if (comment == null || comment.isEmpty()) {
return Result.error("400", "评论内容不能为空");
}
assessmentReportService.addDoctorComment(reportId, comment);
return Result.success();
}
/**
* 更新报告状态为已读
*/
@PutMapping("/read/{reportId}")
public Result markAsRead(@PathVariable Integer reportId) {
assessmentReportService.markAsRead(reportId);
return Result.success();
}
/**
* 获取评估报告详情
*/
@GetMapping("/{reportId}")
public Result getReportById(@PathVariable Integer reportId) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "未登录");
}
// 通过ID获取报告
AssessmentReport report = assessmentReportService.getReportById(reportId);
if (report == null) {
return Result.error("404", "报告不存在");
}
return Result.success(report);
}
}

View File

@@ -0,0 +1,48 @@
package com.example.controller;
import com.example.common.Result;
import com.example.entity.ChatMessage;
import com.example.service.ChatMessageService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 聊天消息控制器
*/
@RestController
@RequestMapping("/chat")
public class ChatMessageController {
@Resource
private ChatMessageService chatMessageService;
/**
* 发送消息
*/
@PostMapping("/send")
public Result send(@RequestBody ChatMessage chatMessage) {
ChatMessage message = chatMessageService.sendMessage(chatMessage);
return Result.success(message);
}
/**
* 获取聊天历史记录
*/
@GetMapping("/history")
public Result getChatHistory(@RequestParam Integer contactId, @RequestParam String contactType) {
List<ChatMessage> messages = chatMessageService.getChatHistory(contactId, contactType);
return Result.success(messages);
}
/**
* 获取聊天联系人列表
*/
@GetMapping("/contacts")
public Result getContactList() {
List<Map<String, Object>> contacts = chatMessageService.getContactList();
return Result.success(contacts);
}
}

View File

@@ -0,0 +1,147 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.Doctor;
import com.example.entity.DoctorPatient;
import com.example.service.DoctorPatientService;
import com.example.service.DoctorService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 医生相关接口
*/
@RestController
@RequestMapping("/doctor")
public class DoctorController {
@Resource
private DoctorService doctorService;
@Resource
private DoctorPatientService doctorPatientService;
/**
* 添加医生
*/
@PostMapping
public Result add(@RequestBody Doctor doctor) {
doctorService.add(doctor);
return Result.success();
}
/**
* 删除医生
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
doctorService.deleteById(id);
return Result.success();
}
/**
* 批量删除医生
*/
@DeleteMapping("/batch")
public Result deleteBatch(@RequestBody List<Integer> ids) {
doctorService.deleteBatch(ids);
return Result.success();
}
/**
* 修改医生
*/
@PutMapping
public Result update(@RequestBody Doctor doctor) {
doctorService.updateById(doctor);
return Result.success();
}
/**
* 根据ID查询医生
*/
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Doctor doctor = doctorService.selectById(id);
return Result.success(doctor);
}
/**
* 查询所有医生
*/
@GetMapping
public Result getAll(Doctor doctor) {
List<Doctor> list = doctorService.selectAll(doctor);
return Result.success(list);
}
/**
* 分页查询医生
*/
@GetMapping("/page")
public Result page(Doctor doctor,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageInfo<Doctor> page = doctorService.selectPage(doctor, pageNum, pageSize);
return Result.success(page);
}
/**
* 分页查询医生与Admin页面保持一致的接口
*/
@GetMapping("/selectPage")
public Result selectPage(
Doctor doctor,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageInfo<Doctor> pageInfo = doctorService.selectPage(doctor, pageNum, pageSize);
return Result.success(pageInfo);
}
/**
* 获取当前医生负责的患者列表
* 此接口是为了兼容之前的路径
* @param name 患者姓名(可选)
* @return 患者列表
*/
@GetMapping("/myPatients")
public Result myPatients(@RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"doctor".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
List<DoctorPatient> list = doctorPatientService.selectByDoctorId(currentUser.getId(), name);
return Result.success(list);
}
/**
* 更新医生个人资料
*/
@PutMapping("/update")
public Result updateProfile(@RequestBody Doctor doctor) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"doctor".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
// 确保只能更新自己的信息
if (!currentUser.getId().equals(doctor.getId())) {
return Result.error("403", "只能更新自己的信息");
}
// 更新个人资料
doctorService.updateById(doctor);
return Result.success();
}
}

View File

@@ -0,0 +1,137 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.DoctorPatient;
import com.example.entity.Patient;
import com.example.service.DoctorPatientService;
import com.example.service.PatientService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 医生与患者关系控制器
*/
@RestController
@RequestMapping("/doctor-patient")
public class DoctorPatientController {
@Resource
private DoctorPatientService doctorPatientService;
@Resource
private PatientService patientService;
/**
* 新增医生患者关系
* @param doctorPatient 医生患者关系信息
* @return 结果
*/
@PostMapping
public Result add(@RequestBody DoctorPatient doctorPatient) {
doctorPatientService.add(doctorPatient);
return Result.success();
}
/**
* 更新医生患者关系
* @param doctorPatient 医生患者关系信息
* @return 结果
*/
@PutMapping
public Result update(@RequestBody DoctorPatient doctorPatient) {
doctorPatientService.update(doctorPatient);
return Result.success();
}
/**
* 删除医生患者关系
* @param id 关系ID
* @return 结果
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
doctorPatientService.delete(id);
return Result.success();
}
/**
* 结束医生患者关系
* @param id 关系ID
* @return 结果
*/
@PutMapping("/end/{id}")
public Result endRelationship(@PathVariable Integer id) {
doctorPatientService.endRelationship(id);
return Result.success();
}
/**
* 获取当前医生的患者列表
* @param name 患者姓名(可选)
* @return 患者列表
*/
@GetMapping("/doctor/patients")
public Result getPatientsByDoctor(@RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"doctor".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
List<DoctorPatient> list = doctorPatientService.selectByDoctorId(currentUser.getId(), name);
return Result.success(list);
}
/**
* 获取当前患者的医生列表
* @param name 医生姓名(可选)
* @return 医生列表
*/
@GetMapping("/patient/doctors")
public Result getDoctorsByPatient(@RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"patient".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
List<DoctorPatient> list = doctorPatientService.selectByPatientId(currentUser.getId(), name);
return Result.success(list);
}
/**
* 根据医生ID获取其患者列表管理员使用
* @param doctorId 医生ID
* @param name 患者姓名(可选)
* @return 患者列表
*/
@GetMapping("/admin/doctor/{doctorId}/patients")
public Result getPatientsByDoctorForAdmin(@PathVariable Integer doctorId, @RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"admin".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
List<DoctorPatient> list = doctorPatientService.selectByDoctorId(doctorId, name);
return Result.success(list);
}
/**
* 根据患者ID获取其医生列表管理员使用
* @param patientId 患者ID
* @param name 医生姓名(可选)
* @return 医生列表
*/
@GetMapping("/admin/patient/{patientId}/doctors")
public Result getDoctorsByPatientForAdmin(@PathVariable Integer patientId, @RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"admin".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
List<DoctorPatient> list = doctorPatientService.selectByPatientId(patientId, name);
return Result.success(list);
}
}

View File

@@ -0,0 +1,102 @@
package com.example.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.example.common.Result;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件上传接口
*/
@RestController
@RequestMapping("/files")
public class FileController {
// 文件上传存储路径
private static final String filePath = System.getProperty("user.dir") + "/file/";
private static final String http = "http://localhost:9090/files/";
/**
* 文件上传
*/
@PostMapping("/upload")
public Result upload(MultipartFile file) {
synchronized (FileController.class) {
String flag = System.currentTimeMillis() + "";
String fileName = file.getOriginalFilename();
try {
if (!FileUtil.isDirectory(filePath)) {
FileUtil.mkdir(filePath);
}
// 文件存储形式:时间戳-文件名
FileUtil.writeBytes(file.getBytes(), filePath + flag + "-" + fileName);
System.out.println(fileName + "--上传成功");
Thread.sleep(1L);
} catch (Exception e) {
System.err.println(fileName + "--文件上传失败");
}
return Result.success(http + flag + "-" + fileName);
}
}
/**
* 获取文件
*
* @param flag
* @param response
*/
@GetMapping("/{flag}")
public void avatarPath(@PathVariable String flag, HttpServletResponse response) {
if (!FileUtil.isDirectory(filePath)) {
FileUtil.mkdir(filePath);
}
OutputStream os;
List<String> fileNames = FileUtil.listFileNames(filePath);
String avatar = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");
try {
if (StrUtil.isNotEmpty(avatar)) {
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(avatar, "UTF-8"));
response.setContentType("application/octet-stream");
byte[] bytes = FileUtil.readBytes(filePath + avatar);
os = response.getOutputStream();
os.write(bytes);
os.flush();
os.close();
}
} catch (Exception e) {
System.out.println("文件下载失败");
}
}
/**
* 删除文件
*
* @param flag
*/
@DeleteMapping("/{flag}")
public void delFile(@PathVariable String flag) {
if (!FileUtil.isDirectory(filePath)) {
FileUtil.mkdir(filePath);
}
List<String> fileNames = FileUtil.listFileNames(filePath);
String filename = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");
FileUtil.del(filePath + filename);
System.out.println("删除文件" + filename + "成功");
}
}

View File

@@ -0,0 +1,256 @@
package com.example.controller;
import cn.hutool.core.util.ObjectUtil;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.Doctor;
import com.example.entity.DoctorPatient;
import com.example.entity.Patient;
import com.example.service.DoctorPatientService;
import com.example.service.PatientService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 患者控制器
*/
@RestController
@RequestMapping("/patient")
public class PatientController {
@Resource
private PatientService patientService;
@Resource
private DoctorPatientService doctorPatientService;
/**
* 新增
*/
@PostMapping
public Result add(@RequestBody Patient patient) {
patientService.add(patient);
return Result.success();
}
/**
* 删除
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
patientService.deleteById(id);
return Result.success();
}
/**
* 更新
*/
@PutMapping
public Result update(@RequestBody Patient patient) {
patientService.updateById(patient);
return Result.success();
}
/**
* 根据ID查询
*/
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Patient patient = patientService.selectById(id);
return Result.success(patient);
}
/**
* 查询全部
*/
@GetMapping
public Result findAll() {
Account currentUser = TokenUtils.getCurrentUser();
List<Patient> patients = new ArrayList<>();
// 管理员可以查看所有患者
if (currentUser != null && "admin".equals(currentUser.getRole())) {
patients = patientService.selectAll(null);
}
// 医生只能查看自己的患者
else if (currentUser != null && "doctor".equals(currentUser.getRole())) {
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), null);
if (!doctorPatients.isEmpty()) {
// 提取患者信息
patients = doctorPatients.stream()
.map(DoctorPatient::getPatient)
.filter(p -> p != null)
.collect(Collectors.toList());
}
}
// 患者只能查看自己的信息
else if (currentUser != null && "patient".equals(currentUser.getRole())) {
Patient patient = patientService.selectById(currentUser.getId());
if (patient != null) {
patients.add(patient);
}
}
return Result.success(patients);
}
/**
* 分页查询
*/
@GetMapping("/page")
public Result findPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String name) {
Account currentUser = TokenUtils.getCurrentUser();
// 管理员可以查看所有患者
if (currentUser != null && "admin".equals(currentUser.getRole())) {
return Result.success(patientService.selectPage(pageNum, pageSize, name));
}
// 医生只能查看自己的患者
else if (currentUser != null && "doctor".equals(currentUser.getRole())) {
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), name);
if (!doctorPatients.isEmpty()) {
// 提取患者信息
List<Patient> patients = doctorPatients.stream()
.map(DoctorPatient::getPatient)
.filter(p -> p != null)
.collect(Collectors.toList());
// 手动进行分页
int total = patients.size();
int fromIndex = (pageNum - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, total);
if (fromIndex >= total) {
patients = new ArrayList<>();
} else {
patients = patients.subList(fromIndex, toIndex);
}
return Result.success(patients);
} else {
return Result.success(new ArrayList<>());
}
}
// 患者只能查看自己的信息
else if (currentUser != null && "patient".equals(currentUser.getRole())) {
Patient patient = patientService.selectById(currentUser.getId());
List<Patient> list = new ArrayList<>();
if (patient != null) {
list.add(patient);
}
return Result.success(list);
}
return Result.error("401", "无权限");
}
/**
* 分页查询与Admin页面保持一致的接口
*/
@GetMapping("/selectPage")
public Result selectPage(
Patient patient,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Account currentUser = TokenUtils.getCurrentUser();
// 管理员可以查看所有患者
if (currentUser != null && "admin".equals(currentUser.getRole())) {
// 使用PageHelper手动创建PageInfo
PageHelper.startPage(pageNum, pageSize);
List<Patient> patients = patientService.selectAll(patient);
PageInfo<Patient> pageInfo = new PageInfo<>(patients);
return Result.success(pageInfo);
}
// 医生只能查看自己的患者
else if (currentUser != null && "doctor".equals(currentUser.getRole())) {
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), patient.getName());
if (!doctorPatients.isEmpty()) {
// 提取患者信息
List<Patient> patients = doctorPatients.stream()
.map(DoctorPatient::getPatient)
.filter(p -> p != null)
.collect(Collectors.toList());
// 手动进行分页
int total = patients.size();
int fromIndex = (pageNum - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, total);
if (fromIndex >= total) {
patients = new ArrayList<>();
} else {
patients = patients.subList(fromIndex, toIndex);
}
// 创建PageInfo对象
PageInfo<Patient> pageInfo = new PageInfo<>();
pageInfo.setList(patients);
pageInfo.setTotal(total);
pageInfo.setPageNum(pageNum);
pageInfo.setPageSize(pageSize);
pageInfo.setPages((total + pageSize - 1) / pageSize);
return Result.success(pageInfo);
} else {
PageInfo<Patient> pageInfo = new PageInfo<>();
pageInfo.setList(new ArrayList<>());
pageInfo.setTotal(0);
return Result.success(pageInfo);
}
}
// 患者只能查看自己的信息
else if (currentUser != null && "patient".equals(currentUser.getRole())) {
Patient currentPatient = patientService.selectById(currentUser.getId());
List<Patient> list = new ArrayList<>();
if (currentPatient != null) {
list.add(currentPatient);
}
// 创建PageInfo对象
PageInfo<Patient> pageInfo = new PageInfo<>();
pageInfo.setList(list);
pageInfo.setTotal(list.size());
pageInfo.setPageNum(1);
pageInfo.setPageSize(pageSize);
pageInfo.setPages(1);
return Result.success(pageInfo);
}
return Result.error("401", "无权限");
}
/**
* 更新患者个人资料
*/
@PutMapping("/update")
public Result updateProfile(@RequestBody Patient patient) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !"patient".equals(currentUser.getRole())) {
return Result.error("401", "无权限");
}
// 确保只能更新自己的信息
if (!currentUser.getId().equals(patient.getId())) {
return Result.error("403", "只能更新自己的信息");
}
// 更新个人资料
patientService.updateById(patient);
return Result.success();
}
}

View File

@@ -0,0 +1,47 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.service.RecommendationService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 推荐系统控制器
*/
@RestController
@RequestMapping("/recommendation")
public class RecommendationController {
@Resource
private RecommendationService recommendationService;
/**
* 获取个性化推荐资源
* @param limit 推荐资源数量默认为4
* @return 推荐资源列表
*/
@GetMapping("/resources")
public Result getRecommendedResources(@RequestParam(defaultValue = "4") Integer limit) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "用户未登录");
}
// 验证用户是否为患者
if (!"patient".equals(currentUser.getRole())) {
return Result.error("403", "只有患者才能获取个性化推荐");
}
// 获取推荐资源
List<Map<String, Object>> recommendations =
recommendationService.getRecommendationsForUser(currentUser.getId(), limit);
return Result.success(recommendations);
}
}

View File

@@ -0,0 +1,86 @@
package com.example.controller;
import com.example.common.Result;
import com.example.entity.ResourceCategory;
import com.example.service.ResourceCategoryService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 资源分类控制器
*/
@RestController
@RequestMapping("/resourceCategory")
public class ResourceCategoryController {
@Resource
private ResourceCategoryService resourceCategoryService;
/**
* 获取所有分类
*/
@GetMapping
public Result findAll() {
List<ResourceCategory> list = resourceCategoryService.findAll();
return Result.success(list);
}
/**
* 根据父分类ID获取子分类
*/
@GetMapping("/parent/{parentId}")
public Result findByParentId(@PathVariable Integer parentId) {
List<ResourceCategory> list = resourceCategoryService.findByParentId(parentId);
return Result.success(list);
}
/**
* 根据ID获取分类
*/
@GetMapping("/{id}")
public Result findById(@PathVariable Integer id) {
ResourceCategory category = resourceCategoryService.findById(id);
return Result.success(category);
}
/**
* 新增分类
*/
@PostMapping
public Result add(@RequestBody ResourceCategory category) {
resourceCategoryService.add(category);
return Result.success();
}
/**
* 更新分类
*/
@PutMapping
public Result update(@RequestBody ResourceCategory category) {
resourceCategoryService.update(category);
return Result.success();
}
/**
* 删除分类
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
resourceCategoryService.delete(id);
return Result.success();
}
/**
* 分页查询
*/
@GetMapping("/selectPage")
public Result selectPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
ResourceCategory category) {
PageInfo<ResourceCategory> pageInfo = resourceCategoryService.selectPage(category, pageNum, pageSize);
return Result.success(pageInfo);
}
}

View File

@@ -0,0 +1,167 @@
package com.example.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.ResourceRating;
import com.example.service.ResourceRatingService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 资源评分控制器
*/
@RestController
@RequestMapping("/resourceRating")
public class ResourceRatingController {
@Resource
private ResourceRatingService resourceRatingService;
/**
* 获取资源评分列表(分页)
*/
@GetMapping("/resource/{resourceId}")
public Result findResourceRatingsPage(
@PathVariable Integer resourceId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
// 查询评分列表
List<ResourceRating> ratings = resourceRatingService.findByResourceId(resourceId);
// 构建分页结果
Map<String, Object> pageResult = new HashMap<>();
int start = (pageNum - 1) * pageSize;
int end = Math.min(start + pageSize, ratings.size());
// 截取当前页数据
List<ResourceRating> pageRatings = ratings.subList(
start < ratings.size() ? start : 0,
end < ratings.size() ? end : 0
);
pageResult.put("list", pageRatings);
pageResult.put("total", ratings.size());
pageResult.put("pageNum", pageNum);
pageResult.put("pageSize", pageSize);
return Result.success(pageResult);
}
/**
* 获取资源平均评分
*/
@GetMapping("/average/{resourceId}")
public Result getAverageRating(@PathVariable Integer resourceId) {
Double averageRating = resourceRatingService.getAverageRating(resourceId);
return Result.success(averageRating);
}
/**
* 获取资源评分数量
*/
@GetMapping("/count/{resourceId}")
public Result getRatingCount(@PathVariable Integer resourceId) {
Integer count = resourceRatingService.getRatingCount(resourceId);
return Result.success(count);
}
/**
* 添加或更新评分
*/
@PostMapping
public Result add(@RequestBody ResourceRating resourceRating) {
resourceRatingService.rateResource(resourceRating);
return Result.success();
}
/**
* 获取当前用户的所有评分
*/
@GetMapping("/user")
public Result getUserRatings() {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "用户未登录");
}
// 验证用户是否为患者
if (!"patient".equals(currentUser.getRole())) {
return Result.error("403", "只有患者可以查看自己的评分");
}
// 获取用户评分列表
List<ResourceRating> ratings = resourceRatingService.findByPatientId(currentUser.getId());
return Result.success(ratings);
}
/**
* 获取所有评分数据(用于推荐算法)
*/
@GetMapping("/all")
public Result getAllRatings() {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "用户未登录");
}
// 验证用户是否为患者
if (!"patient".equals(currentUser.getRole())) {
return Result.error("403", "只有患者可以访问评分数据");
}
// 获取所有评分列表
List<ResourceRating> ratings = resourceRatingService.findAll();
return Result.success(ratings);
}
/**
* 获取资源的平均评分和评分数量
*/
@GetMapping("/resource/{resourceId}/stats")
public Result getResourceRatingStats(@PathVariable Integer resourceId) {
Double averageRating = resourceRatingService.getAverageRating(resourceId);
Integer count = resourceRatingService.getRatingCount(resourceId);
Map<String, Object> stats = new HashMap<>();
stats.put("averageRating", averageRating);
stats.put("ratingCount", count);
return Result.success(stats);
}
/**
* 删除评价
*/
@DeleteMapping("/{id}")
public Result deleteRating(@PathVariable Integer id) {
resourceRatingService.deleteRating(id);
return Result.success();
}
/**
* 根据资源ID查询评分记录
*/
@GetMapping("/{resourceId}")
public Result findByResourceId(@PathVariable Integer resourceId) {
List<ResourceRating> ratings = resourceRatingService.findByResourceId(resourceId);
double averageRating = resourceRatingService.getAverageRating(resourceId);
Integer count = resourceRatingService.getRatingCount(resourceId);
Map<String, Object> result = new HashMap<>();
result.put("ratings", ratings);
result.put("averageRating", averageRating);
result.put("count", count != null ? count : 0);
return Result.success(result);
}
}

View File

@@ -0,0 +1,91 @@
package com.example.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.Result;
import com.example.entity.SymptomRecord;
import com.example.service.SymptomRecordService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 症状记录控制器
*/
@RestController
@RequestMapping("/symptomRecord")
public class SymptomRecordController {
@Resource
private SymptomRecordService symptomRecordService;
/**
* 添加症状记录
*/
@PostMapping
public Result add(@RequestBody SymptomRecord symptomRecord) {
symptomRecordService.add(symptomRecord);
return Result.success();
}
/**
* 更新症状记录
*/
@PutMapping
public Result update(@RequestBody SymptomRecord symptomRecord) {
symptomRecordService.update(symptomRecord);
return Result.success();
}
/**
* 删除症状记录
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
symptomRecordService.delete(id);
return Result.success();
}
/**
* 根据ID查询症状记录
*/
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
SymptomRecord symptomRecord = symptomRecordService.getById(id);
return Result.success(symptomRecord);
}
/**
* 根据患者ID查询症状记录
*/
@GetMapping("/patient/{patientId}")
public Result getByPatientId(@PathVariable Integer patientId) {
List<SymptomRecord> list = symptomRecordService.getByPatientId(patientId);
return Result.success(list);
}
/**
* 查询当前登录患者的症状记录
*/
@GetMapping("/my")
public Result getMyRecords() {
List<SymptomRecord> list = symptomRecordService.getByPatientId(null);
return Result.success(list);
}
/**
* 分页查询症状记录
*/
@GetMapping("/selectPage")
public Result selectPage(
@RequestParam(required = false) String patientName,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
SymptomRecord symptomRecord = new SymptomRecord();
symptomRecord.setPatientName(patientName);
com.github.pagehelper.PageInfo<SymptomRecord> pageInfo = symptomRecordService.selectPage(symptomRecord, pageNum, pageSize, startDate, endDate);
return Result.success(pageInfo);
}
}

View File

@@ -0,0 +1,66 @@
package com.example.controller;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.service.SymptomRecordService;
import jakarta.annotation.Resource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.Map;
/**
* 症状记录统计控制器
*/
@RestController
@RequestMapping("/symptomStatistics")
public class SymptomStatisticsController {
@Resource
private SymptomRecordService symptomRecordService;
/**
* 获取症状记录统计数据
*/
@GetMapping
public Result getStatistics(
@RequestParam(required = false) Integer patientId,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "请先登录");
}
Map<String, Object> result;
// 根据用户角色获取不同的统计数据
switch (currentUser.getRole()) {
case "admin":
// 管理员可以查看所有数据或指定患者的数据
result = symptomRecordService.getStatisticsData(patientId, startDate, endDate);
break;
case "doctor":
// 如果请求包含patientId且该患者属于当前医生则返回该患者的数据
// 否则返回该医生的所有患者数据
if (patientId != null) {
result = symptomRecordService.getStatisticsData(patientId, startDate, endDate);
} else {
result = symptomRecordService.getStatisticsByDoctor(currentUser.getId(), startDate, endDate);
}
break;
case "patient":
// 患者只能查看自己的数据
result = symptomRecordService.getStatisticsData(currentUser.getId(), startDate, endDate);
break;
default:
return Result.error("403", "无权限访问");
}
return Result.success(result);
}
}

View File

@@ -0,0 +1,220 @@
package com.example.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.Result;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.ResourceCategory;
import com.example.entity.TreatmentResource;
import com.example.service.ResourceCategoryService;
import com.example.service.TreatmentResourceService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 心理治疗资源控制器
*/
@RestController
@RequestMapping("/treatmentResource")
public class TreatmentResourceController {
@Resource
private TreatmentResourceService treatmentResourceService;
@Resource
private ResourceCategoryService resourceCategoryService;
/**
* 分页搜索资源
*/
@GetMapping("/search")
public Result search(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String keyword) {
// 获取当前用户角色
Account currentUser = TokenUtils.getCurrentUser();
String role = currentUser != null ? currentUser.getRole() : "patient";
Page<TreatmentResource> page = new Page<>(pageNum, pageSize);
page = treatmentResourceService.search(page, keyword, role);
return Result.success(page);
}
/**
* 分页查询
*/
@GetMapping("/selectPage")
public Result selectPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
TreatmentResource resource) {
PageInfo<TreatmentResource> pageInfo = treatmentResourceService.selectPage(resource, pageNum, pageSize);
return Result.success(pageInfo);
}
/**
* 根据分类获取资源
*/
@GetMapping("/category/{categoryId}")
public Result findByCategoryId(@PathVariable Integer categoryId) {
List<TreatmentResource> list = treatmentResourceService.findByCategoryId(categoryId);
return Result.success(list);
}
/**
* 根据ID获取资源
*/
@GetMapping("/{id}")
public Result findById(@PathVariable Integer id) {
TreatmentResource resource = treatmentResourceService.findById(id);
treatmentResourceService.incrementVisitCount(id);
return Result.success(resource);
}
/**
* 新增资源
*/
@PostMapping
public Result add(@RequestBody TreatmentResource resource) {
treatmentResourceService.add(resource);
return Result.success();
}
/**
* 更新资源
*/
@PutMapping
public Result update(@RequestBody TreatmentResource resource) {
treatmentResourceService.update(resource);
return Result.success();
}
/**
* 删除资源
*/
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
treatmentResourceService.delete(id);
return Result.success();
}
/**
* 审核资源
*/
@PutMapping("/audit/{id}")
public Result audit(@PathVariable Integer id, @RequestParam String status) {
treatmentResourceService.changeAuditStatus(id, status);
return Result.success();
}
/**
* 下载资源
*/
@GetMapping("/download/{id}")
public Result download(@PathVariable Integer id) {
TreatmentResource resource = treatmentResourceService.findById(id);
if (resource != null) {
treatmentResourceService.incrementDownloadCount(id);
}
return Result.success(resource);
}
/**
* 增加资源访问次数(新路径)
*/
@PutMapping("/incrementVisit/{id}")
public Result incrementVisit(@PathVariable Integer id) {
treatmentResourceService.incrementVisitCount(id);
return Result.success();
}
/**
* 增加资源下载次数(新路径)
*/
@PutMapping("/incrementDownload/{id}")
public Result incrementDownload(@PathVariable Integer id) {
treatmentResourceService.incrementDownloadCount(id);
return Result.success();
}
/**
* 查询医生自己发布的资源
*/
@GetMapping("/myResources")
public Result myResources(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
TreatmentResource resource) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "未登录");
}
// 只有医生可以查询自己发布的资源
if (!"doctor".equals(currentUser.getRole())) {
return Result.error("403", "无权限查询");
}
// 强制设置发布者ID为当前登录用户
resource.setPublisherId(currentUser.getId());
// 使用PageHelper进行分页
PageInfo<TreatmentResource> pageInfo = treatmentResourceService.findByPublisher(resource, pageNum, pageSize);
return Result.success(pageInfo);
}
/**
* 获取资源使用情况统计数据
*/
@GetMapping("/statistics")
public Result getStatistics() {
return Result.success(treatmentResourceService.getResourceStatistics());
}
/**
* 获取所有已审核通过的心理资源
*/
@GetMapping("/all")
public Result getAllApprovedResources(@RequestParam(required = false) String auditStatus) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
return Result.error("401", "用户未登录");
}
// 查询关键词为空,表示获取所有资源
String keyword = "";
// 根据审核状态查询资源
List<TreatmentResource> resources;
if (auditStatus != null && !auditStatus.isEmpty()) {
// 使用TreatmentResourceService的selectPage方法
TreatmentResource queryParam = new TreatmentResource();
queryParam.setAuditStatus(auditStatus);
// 这里不使用分页所以传一个很大的pageSize
PageInfo<TreatmentResource> pageInfo = treatmentResourceService.selectPage(queryParam, 1, 1000);
resources = pageInfo.getList();
} else {
// 默认只返回已通过审核的资源
TreatmentResource queryParam = new TreatmentResource();
queryParam.setAuditStatus("已通过");
// 这里不使用分页所以传一个很大的pageSize
PageInfo<TreatmentResource> pageInfo = treatmentResourceService.selectPage(queryParam, 1, 1000);
resources = pageInfo.getList();
}
return Result.success(resources);
}
}

View File

@@ -0,0 +1,93 @@
package com.example.controller;
import com.example.common.config.TokenUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.example.common.Result;
import com.example.entity.*;
import com.example.service.*;
import com.example.exception.CustomException;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.*;
/**
* 描述:系统用户操作相关接口
*/
@RestController
public class WebController {
@Resource
private AdminService adminService;
@Resource
private DoctorService doctorService;
@Resource
private PatientService patientService;
/**
* 描述:用户登录接口
*/
@PostMapping("/login")
public Result login(@RequestBody Account account) {
Account loginAccount = null;
if ("admin".equals(account.getRole())) {
loginAccount = adminService.login(account);
}
if ("doctor".equals(account.getRole())) {
loginAccount = doctorService.login(account);
}
if ("patient".equals(account.getRole())) {
loginAccount = patientService.login(account);
}
return Result.success(loginAccount);
}
/**
* 描述:用户注册接口
*/
@PostMapping("/register")
public Result register(@RequestBody Account account) {
if ("admin".equals(account.getRole())) {
adminService.register(account);
}
if ("doctor".equals(account.getRole())) {
doctorService.register(account);
}
if ("patient".equals(account.getRole())) {
patientService.register(account);
}
return Result.success();
}
/**
* 描述:更新密码接口
*/
@PutMapping("/updatePassword")
public Result updatePassword(@RequestBody Account account) {
if ("admin".equals(account.getRole())) {
adminService.updatePassword(account);
}
if ("doctor".equals(account.getRole())) {
doctorService.updatePassword(account);
}
if ("patient".equals(account.getRole())) {
patientService.updatePassword(account);
}
return Result.success();
}
}

View File

@@ -0,0 +1,30 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
@Data
public class Account {
private Integer id;
/** 账号 */
private String username;
/** 密码 */
private String password;
/** 姓名 */
private String name;
/** 性别 */
private String sex;
/** 手机号 */
private String phone;
/** 邮箱 */
private String email;
/** 角色 */
private String role;
@TableField(exist = false)
private String newPassword;
@TableField(exist = false)
private String token;
/** 医生ID (用于患者注册时选择医生) */
@TableField(exist = false)
private Integer doctorId;
}

View File

@@ -0,0 +1,33 @@
package com.example.entity;
import cn.hutool.core.annotation.Alias;
import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
/**
* 管理员
*/
@Data
@TableName("admin")
public class Admin extends Account {
/** id */
@TableId(type = IdType.AUTO)
private Integer id;
/** 账号 */
private String username;
/** 密码 */
private String password;
/** 名称 */
private String name;
/** 头像 */
private String avatar;
/** 性别 */
private String sex;
/** 手机号 */
private String phone;
}

View File

@@ -0,0 +1,59 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
/**
* 公告实体类
*/
@Data
@TableName("announcement")
public class Announcement {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/**
* 是否置顶
*/
private Boolean isTop;
/**
* 状态1-发布0-草稿)
*/
private Integer status;
/**
* 发布人ID
*/
private Integer publisherId;
/**
* 发布人姓名
*/
private String publisherName;
}

View File

@@ -0,0 +1,41 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("assessment_report")
public class AssessmentReport {
@TableId(type = IdType.AUTO)
private Integer id;
private Integer patientId;
private String reportPeriod;
private LocalDate startDate;
private LocalDate endDate;
private String summary;
private String trendAnalysis;
private String recommendation;
private String doctorComment;
private String status;
private LocalDateTime createTime;
// 非数据库字段,用于前端显示
private String patientName;
private String doctorName;
}

View File

@@ -0,0 +1,92 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.util.Date;
/**
* 聊天消息实体类
*/
@Data
@TableName("chat_message")
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性
public class ChatMessage {
/**
* 消息ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 发送者ID
*/
private Integer senderId;
/**
* 接收者ID
*/
private Integer receiverId;
/**
* 发送者类型(doctor/patient)
*/
private String senderType;
/**
* 接收者类型(doctor/patient)
*/
private String receiverType;
/**
* 消息内容
*/
private String content;
/**
* 发送时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date sendTime;
/**
* 是否已读(0-未读,1-已读)
*/
private Boolean isRead;
/**
* 阅读时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date readTime;
/**
* 发送者名称(非数据库字段)
*/
@TableField(exist = false)
private String senderName;
/**
* 接收者名称(非数据库字段)
*/
@TableField(exist = false)
private String receiverName;
/**
* 发送者头像(非数据库字段)
*/
@TableField(exist = false)
private String senderAvatar;
/**
* 临时消息ID(用于前端标识临时消息,非数据库字段)
*/
@TableField(exist = false)
private String tempId;
}

View File

@@ -0,0 +1,41 @@
package com.example.entity;
import cn.hutool.core.annotation.Alias;
import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
/**
* 医生
*/
@Data
@TableName("doctor")
public class Doctor extends Account {
/** id */
@TableId(type = IdType.AUTO)
private Integer id;
/** 账号 */
private String username;
/** 密码 */
private String password;
/** 名称 */
private String name;
/** 头像 */
private String avatar;
/** 性别 */
private String sex;
/** 手机号 */
private String phone;
/** 邮箱 */
private String email;
/** 执业证书编号 */
private String certificateNo;
/** 所属医院 */
private String hospital;
/** 科室 */
private String department;
}

View File

@@ -0,0 +1,56 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@TableName("doctor_patient")
@Data
public class DoctorPatient {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 医生ID
*/
private Integer doctorId;
/**
* 患者ID
*/
private Integer patientId;
/**
* 关系建立时间
*/
private Date relationshipStart;
/**
* 状态(正常/已结束)
*/
private String status;
/**
* 创建时间
*/
private Date createTime;
/**
* 医生信息
*/
@TableField(exist = false)
private Doctor doctor;
/**
* 患者信息
*/
@TableField(exist = false)
private Patient patient;
}

View File

@@ -0,0 +1,35 @@
package com.example.entity;
import cn.hutool.core.annotation.Alias;
import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
/**
* 患者
*/
@Data
@TableName("patient")
public class Patient extends Account {
/** id */
@TableId(type = IdType.AUTO)
private Integer id;
/** 账号 */
private String username;
/** 密码 */
private String password;
/** 名称 */
private String name;
/** 头像 */
private String avatar;
/** 性别 */
private String sex;
/** 手机号 */
private String phone;
/** 邮箱 */
private String email;
}

View File

@@ -0,0 +1,17 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("resource_category")
public class ResourceCategory {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String description;
private Integer parentId;
}

View File

@@ -0,0 +1,57 @@
package com.example.entity;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 资源评分实体类
*/
@Data
@TableName("resource_rating")
public class ResourceRating {
/**
* ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 资源ID
*/
private Integer resourceId;
/**
* 患者ID
*/
private Integer patientId;
/**
* 评分(1-5)
*/
private Integer rating;
/**
* 评价内容
*/
private String comment;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 患者姓名 (非数据库字段)
*/
@TableField(exist = false)
private String patientName;
@TableField(exist = false)
private String resourceTitle;
}

View File

@@ -0,0 +1,121 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@TableName("symptom_record")
@Data
public class SymptomRecord {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 患者ID
*/
private Integer patientId;
/**
* 症状描述
*/
private String symptomDesc;
/**
* 严重程度等级(1-10)
*/
private Integer severityLevel;
/**
* 情绪基线值(1-10)
*/
private Integer emotionScore;
/**
* 活力持续时间(小时)
*/
private Integer energyDuration;
/**
* 兴趣恢复度(百分比)
*/
private Integer interestRecovery;
/**
* 入睡所需时间(分钟)
*/
private Integer sleepTime;
/**
* 夜间觉醒次数
*/
private Integer wakeCount;
/**
* 晨醒时间
*/
private String morningWake;
/**
* 进食完成度(百分比)
*/
private Integer mealCompletion;
/**
* 疼痛位置
*/
private String painLocation;
/**
* 疼痛强度(1-10)
*/
private Integer painIntensity;
/**
* 短期记忆测试成功数(0-3)
*/
private Integer memoryTest;
/**
* 决策耗时(分钟)
*/
private Integer decisionTime;
/**
* 注意力跨度(分钟)
*/
private Integer attentionSpan;
/**
* 自杀意念频率(次数)
*/
private Integer suicidalThought;
/**
* 记录时间
*/
private Date recordTime;
/**
* 创建时间
*/
private Date createTime;
/**
* 患者信息(非数据库字段)
*/
@TableField(exist = false)
private Patient patient;
/**
* 患者名称(非数据库字段,用于搜索)
*/
@TableField(exist = false)
private String patientName;
}

View File

@@ -0,0 +1,33 @@
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("treatment_resource")
public class TreatmentResource {
@TableId(type = IdType.AUTO)
private Integer id;
private String title;
private Integer categoryId;
private String content;
private String resourceUrl;
private String applicableSymptoms;
private Integer visitCount;
private Integer downloadCount;
private Integer publisherId;
private String auditStatus;
private LocalDateTime createTime;
@TableField(exist = false)
private String categoryName;
@TableField(exist = false)
private String publisherName;
}

View File

@@ -0,0 +1,35 @@
package com.example.exception;
import com.example.common.enums.ResultCodeEnum;
public class CustomException extends RuntimeException {
private String code;
private String msg;
public CustomException(ResultCodeEnum resultCodeEnum) {
this.code = resultCodeEnum.code;
this.msg = resultCodeEnum.msg;
}
public CustomException(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

View File

@@ -0,0 +1,30 @@
package com.example.exception;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.example.common.Result;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice(basePackages="com.example.controller")
public class GlobalExceptionHandler {
private static final Log log = LogFactory.get();
//统一异常处理@ExceptionHandler,主要用于Exception
@ExceptionHandler(Exception.class)
@ResponseBody//返回json串
public Result error(HttpServletRequest request, Exception e){
log.error("异常信息:",e);
return Result.error();
}
@ExceptionHandler(CustomException.class)
@ResponseBody//返回json串
public Result customError(HttpServletRequest request, CustomException e){
return Result.error(e.getCode(), e.getMsg());
}
}

View File

@@ -0,0 +1,32 @@
package com.example.mapper;
import com.example.entity.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface AdminMapper extends BaseMapper<Admin> {
/**
* 查询所有
*/
List<Admin> selectAll(Admin admin);
/**
* 根据ID查询
*/
Admin selectById(Integer id);
/**
* 删除
*/
int deleteById(Integer id);
@Select("select * from admin where `username` = #{name}")
Admin selectByUsername(@Param("name") String userName);
}

View File

@@ -0,0 +1,41 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Announcement;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 公告Mapper
*/
public interface AnnouncementMapper extends BaseMapper<Announcement> {
/**
* 查询所有已发布的公告
* @return 公告列表
*/
@Select("SELECT * FROM announcement WHERE status = 1 ORDER BY is_top DESC, create_time DESC")
List<Announcement> selectAllPublished();
/**
* 查询所有已发布的公告(分页)
* @param title 标题关键字
* @return 公告列表
*/
@Select("<script>SELECT * FROM announcement WHERE status = 1 " +
"<if test=\"title != null and title != ''\">AND title LIKE CONCAT('%', #{title}, '%')</if>" +
" ORDER BY is_top DESC, create_time DESC</script>")
List<Announcement> selectAllPublishedWithKeyword(@Param("title") String title);
/**
* 查询所有公告(包括草稿,仅管理员可用)
* @param title 标题关键字
* @return 公告列表
*/
@Select("<script>SELECT * FROM announcement " +
"<if test=\"title != null and title != ''\">WHERE title LIKE CONCAT('%', #{title}, '%')</if>" +
" ORDER BY is_top DESC, create_time DESC</script>")
List<Announcement> selectAllForAdmin(@Param("title") String title);
}

View File

@@ -0,0 +1,50 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.AssessmentReport;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface AssessmentReportMapper extends BaseMapper<AssessmentReport> {
/**
* 查询患者的评估报告列表
*/
@Select("SELECT ar.*, p.name as patientName " +
"FROM assessment_report ar " +
"LEFT JOIN patient p ON ar.patient_id = p.id " +
"WHERE ar.patient_id = #{patientId} " +
"ORDER BY ar.create_time DESC")
List<AssessmentReport> selectByPatientId(@Param("patientId") Integer patientId);
/**
* 查询医生负责的患者的评估报告列表
*/
@Select("SELECT ar.*, p.name as patientName " +
"FROM assessment_report ar " +
"LEFT JOIN patient p ON ar.patient_id = p.id " +
"INNER JOIN doctor_patient dp ON ar.patient_id = dp.patient_id " +
"WHERE dp.doctor_id = #{doctorId} " +
"ORDER BY ar.create_time DESC")
List<AssessmentReport> selectByDoctorId(@Param("doctorId") Integer doctorId);
/**
* 查询所有评估报告(管理员使用)
*/
@Select("SELECT ar.*, p.name as patientName " +
"FROM assessment_report ar " +
"LEFT JOIN patient p ON ar.patient_id = p.id " +
"ORDER BY ar.create_time DESC")
List<AssessmentReport> selectAllReports();
/**
* 根据ID查询评估报告
*/
@Select("SELECT id, patient_id, report_period, start_date, end_date, summary, trend_analysis, " +
"recommendation, doctor_comment, status, create_time " +
"FROM assessment_report " +
"WHERE id = #{id}")
AssessmentReport selectById(@Param("id") Integer id);
}

View File

@@ -0,0 +1,51 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.ChatMessage;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@Mapper
public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
/**
* 查询两个用户之间的聊天记录
*/
@Select("SELECT * FROM chat_message WHERE " +
"((sender_id = #{userId1} AND sender_type = #{userType1} AND receiver_id = #{userId2} AND receiver_type = #{userType2}) OR " +
"(sender_id = #{userId2} AND sender_type = #{userType2} AND receiver_id = #{userId1} AND receiver_type = #{userType1})) " +
"ORDER BY send_time ASC")
List<ChatMessage> selectChatHistory(@Param("userId1") Integer userId1, @Param("userType1") String userType1,
@Param("userId2") Integer userId2, @Param("userType2") String userType2);
/**
* 将消息标记为已读
*/
@Update("UPDATE chat_message SET is_read = 1, read_time = NOW() " +
"WHERE receiver_id = #{receiverId} AND receiver_type = #{receiverType} " +
"AND sender_id = #{senderId} AND sender_type = #{senderType} AND is_read = 0")
int markAsRead(@Param("receiverId") Integer receiverId, @Param("receiverType") String receiverType,
@Param("senderId") Integer senderId, @Param("senderType") String senderType);
/**
* 查询用户的聊天列表(最近联系人)
*/
@Select("SELECT DISTINCT " +
"CASE WHEN sender_id = #{userId} AND sender_type = #{userType} THEN receiver_id ELSE sender_id END AS contact_id, " +
"CASE WHEN sender_id = #{userId} AND sender_type = #{userType} THEN receiver_type ELSE sender_type END AS contact_type " +
"FROM chat_message " +
"WHERE (sender_id = #{userId} AND sender_type = #{userType}) OR (receiver_id = #{userId} AND receiver_type = #{userType}) " +
"ORDER BY MAX(send_time) DESC")
List<Object[]> selectChatContacts(@Param("userId") Integer userId, @Param("userType") String userType);
/**
* 统计未读消息数
*/
@Select("SELECT COUNT(*) FROM chat_message WHERE receiver_id = #{receiverId} AND receiver_type = #{receiverType} AND sender_id = #{senderId} AND sender_type = #{senderType} AND is_read = 0")
int countUnreadMessages(@Param("receiverId") Integer receiverId, @Param("receiverType") String receiverType,
@Param("senderId") Integer senderId, @Param("senderType") String senderType);
}

View File

@@ -0,0 +1,29 @@
package com.example.mapper;
import com.example.entity.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface DoctorMapper extends BaseMapper<Doctor> {
/**
* 查询所有
*/
List<Doctor> selectAll(Doctor doctor);
/**
* 根据ID查询
*/
Doctor selectById(Integer id);
/**
* 删除
*/
int deleteById(Integer id);
@Select("select * from doctor where `username` = #{name}")
Doctor selectByUsername(@Param("name") String userName);
}

View File

@@ -0,0 +1,28 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.DoctorPatient;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface DoctorPatientMapper extends BaseMapper<DoctorPatient> {
/**
* 根据医生ID查询关联的患者
* @param doctorId 医生ID
* @param name 患者姓名(可选)
* @return 患者列表
*/
List<DoctorPatient> selectByDoctorId(@Param("doctorId") Integer doctorId, @Param("name") String name);
/**
* 根据患者ID查询关联的医生
* @param patientId 患者ID
* @param name 医生姓名(可选)
* @return 医生列表
*/
List<DoctorPatient> selectByPatientId(@Param("patientId") Integer patientId, @Param("name") String name);
}

View File

@@ -0,0 +1,29 @@
package com.example.mapper;
import com.example.entity.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface PatientMapper extends BaseMapper<Patient> {
/**
* 查询所有
*/
List<Patient> selectAll(Patient patient);
/**
* 根据ID查询
*/
Patient selectById(Integer id);
/**
* 删除
*/
int deleteById(Integer id);
@Select("select * from patient where `username` = #{name}")
Patient selectByUsername(@Param("name") String userName);
}

View File

@@ -0,0 +1,20 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.ResourceCategory;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ResourceCategoryMapper extends BaseMapper<ResourceCategory> {
@Select("SELECT * FROM resource_category WHERE parent_id = #{parentId}")
List<ResourceCategory> selectByParentId(Integer parentId);
@Select("SELECT * FROM resource_category ORDER BY parent_id, id")
List<ResourceCategory> selectAllCategories();
@Select("SELECT * FROM resource_category WHERE name LIKE CONCAT('%', #{name}, '%') ORDER BY parent_id, id")
List<ResourceCategory> selectPage(@Param("name") String name);
}

View File

@@ -0,0 +1,46 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.ResourceRating;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ResourceRatingMapper extends BaseMapper<ResourceRating> {
/**
* 根据资源ID查询评分记录包含用户名信息
*/
@Select("SELECT r.*, p.name as patient_name " +
"FROM resource_rating r " +
"LEFT JOIN patient p ON r.patient_id = p.id " +
"WHERE r.resource_id = #{resourceId} " +
"ORDER BY r.create_time DESC")
List<ResourceRating> selectByResourceId(@Param("resourceId") Integer resourceId);
/**
* 查询用户是否已对某资源评分
*/
@Select("SELECT COUNT(*) FROM resource_rating WHERE resource_id = #{resourceId} AND patient_id = #{patientId}")
int countByResourceIdAndPatientId(@Param("resourceId") Integer resourceId, @Param("patientId") Integer patientId);
/**
* 获取资源的平均评分
*/
@Select("SELECT IFNULL(AVG(rating), 0) FROM resource_rating WHERE resource_id = #{resourceId}")
double getAverageRating(@Param("resourceId") Integer resourceId);
/**
* 获取资源的评价数量
*/
@Select("SELECT COUNT(*) FROM resource_rating WHERE resource_id = #{resourceId}")
Integer getRatingCount(@Param("resourceId") Integer resourceId);
/**
* 根据资源ID和患者ID查询评分记录
*/
@Select("SELECT * FROM resource_rating WHERE resource_id = #{resourceId} AND patient_id = #{patientId} LIMIT 1")
ResourceRating findByResourceAndPatient(@Param("resourceId") Integer resourceId, @Param("patientId") Integer patientId);
}

View File

@@ -0,0 +1,36 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.SymptomRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDate;
import java.util.List;
/**
* 症状记录Mapper接口
*/
@Mapper
public interface SymptomRecordMapper extends BaseMapper<SymptomRecord> {
/**
* 查询症状记录并关联患者信息
* @param patientId 患者ID (可选)
* @return 症状记录列表
*/
List<SymptomRecord> selectWithPatient(@Param("patientId") Integer patientId);
/**
* 根据患者ID和日期范围查询症状记录
*/
@Select("SELECT * FROM symptom_record " +
"WHERE patient_id = #{patientId} " +
"AND record_time BETWEEN #{startDate} AND #{endDate} " +
"ORDER BY record_time ASC")
List<SymptomRecord> selectByPatientIdAndDateRange(
@Param("patientId") Integer patientId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);
}

View File

@@ -0,0 +1,67 @@
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.TreatmentResource;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface TreatmentResourceMapper extends BaseMapper<TreatmentResource> {
@Select("SELECT r.*, c.name as category_name " +
"FROM treatment_resource r " +
"LEFT JOIN resource_category c ON r.category_id = c.id " +
"WHERE r.audit_status = '已通过' " +
"AND (r.title LIKE CONCAT('%', #{keyword}, '%') " +
"OR r.applicable_symptoms LIKE CONCAT('%', #{keyword}, '%')) " +
"ORDER BY r.create_time DESC")
Page<TreatmentResource> searchResourcesForPatient(Page<TreatmentResource> page, @Param("keyword") String keyword);
@Select("SELECT r.*, c.name as category_name " +
"FROM treatment_resource r " +
"LEFT JOIN resource_category c ON r.category_id = c.id " +
"WHERE (r.title LIKE CONCAT('%', #{keyword}, '%') " +
"OR r.applicable_symptoms LIKE CONCAT('%', #{keyword}, '%')) " +
"ORDER BY r.create_time DESC")
Page<TreatmentResource> searchResourcesForAdmin(Page<TreatmentResource> page, @Param("keyword") String keyword);
@Select("SELECT r.*, c.name as category_name " +
"FROM treatment_resource r " +
"LEFT JOIN resource_category c ON r.category_id = c.id " +
"WHERE (r.title LIKE CONCAT('%', #{keyword}, '%') " +
"OR r.applicable_symptoms LIKE CONCAT('%', #{keyword}, '%')) " +
"ORDER BY r.create_time DESC")
List<TreatmentResource> selectAll(@Param("keyword") String keyword);
@Select("SELECT r.*, c.name as category_name " +
"FROM treatment_resource r " +
"LEFT JOIN resource_category c ON r.category_id = c.id " +
"WHERE r.audit_status = '已通过' " +
"AND (r.title LIKE CONCAT('%', #{keyword}, '%') " +
"OR r.applicable_symptoms LIKE CONCAT('%', #{keyword}, '%')) " +
"ORDER BY r.create_time DESC")
List<TreatmentResource> selectAllAudited(@Param("keyword") String keyword);
@Select("SELECT r.*, c.name as category_name " +
"FROM treatment_resource r " +
"LEFT JOIN resource_category c ON r.category_id = c.id " +
"WHERE r.category_id = #{categoryId} " +
"AND r.audit_status = '已通过' " +
"ORDER BY r.create_time DESC")
List<TreatmentResource> selectByCategoryId(@Param("categoryId") Integer categoryId);
@Update("UPDATE treatment_resource SET visit_count = visit_count + 1 WHERE id = #{id}")
int incrementVisitCount(@Param("id") Integer id);
@Update("UPDATE treatment_resource SET download_count = download_count + 1 WHERE id = #{id}")
int incrementDownloadCount(@Param("id") Integer id);
/**
* 根据发布者ID查询所有资源
*/
@Select("SELECT * FROM treatment_resource WHERE publisher_id = #{publisherId} AND (title LIKE CONCAT('%', #{keyword}, '%') OR applicable_symptoms LIKE CONCAT('%', #{keyword}, '%'))")
List<TreatmentResource> selectByPublisher(@Param("publisherId") Integer publisherId, @Param("keyword") String keyword);
}

View File

@@ -0,0 +1,171 @@
package com.example.service;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.exception.CustomException;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class AIChatService {
private static final Logger log = LoggerFactory.getLogger(AIChatService.class);
@Resource
private RestTemplate restTemplate;
@Value("${dashscope.api-key}")
private String apiKey;
@Value("${dashscope.model}")
private String model;
private final String API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
/**
* 发送消息到通义千问API并获取回复
*/
public String sendMessage(String message) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
log.info("准备发送消息到通义千问API用户ID: {}, 消息内容: {}", currentUser.getId(), message);
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + apiKey);
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model);
// 添加参数
Map<String, Object> parameters = new HashMap<>();
parameters.put("temperature", 0.7);
parameters.put("top_p", 0.8);
parameters.put("result_format", "message");
requestBody.put("parameters", parameters);
// 添加用户消息
Map<String, Object> input = new HashMap<>();
List<Map<String, Object>> messages = new ArrayList<>();
Map<String, Object> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
messages.add(userMessage);
input.put("messages", messages);
requestBody.put("input", input);
try {
// 记录完整请求
log.info("发送请求到API: {}", API_URL);
log.info("请求体: {}", requestBody);
// 发送请求
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Map> response = restTemplate.exchange(API_URL, HttpMethod.POST, requestEntity, Map.class);
// 记录响应
log.info("API响应状态码: {}", response.getStatusCode());
log.debug("API响应内容: {}", response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
Map<String, Object> responseBody = response.getBody();
if (responseBody != null) {
// 检查错误字段
if (responseBody.containsKey("code") && !responseBody.get("code").equals(200) && responseBody.containsKey("message")) {
String errorMessage = (String) responseBody.get("message");
log.error("API返回错误: {}", errorMessage);
return "API错误: " + errorMessage;
}
// 尝试多种可能的响应格式
String content = extractContentFromResponse(responseBody);
if (content != null) {
return content;
}
}
log.warn("无法从API响应中提取内容: {}", responseBody);
return "抱歉,我无法理解您的问题。";
} else {
log.error("API响应错误状态码: {}", response.getStatusCode());
throw new CustomException(ResultCodeEnum.SYSTEM_ERROR.code, "AI服务异常请稍后再试");
}
} catch (Exception e) {
log.error("调用通义千问API出错: ", e);
throw new CustomException(ResultCodeEnum.SYSTEM_ERROR.code, "AI服务异常: " + e.getMessage());
}
}
/**
* 尝试从不同格式的响应中提取内容
*/
private String extractContentFromResponse(Map<String, Object> responseBody) {
// 格式1: output.choices[0].message.content
try {
if (responseBody.containsKey("output")) {
Map<String, Object> output = (Map<String, Object>) responseBody.get("output");
if (output.containsKey("choices")) {
List<Map<String, Object>> choices = (List<Map<String, Object>>) output.get("choices");
if (choices != null && !choices.isEmpty()) {
Map<String, Object> choice = choices.get(0);
if (choice.containsKey("message")) {
Map<String, Object> message = (Map<String, Object>) choice.get("message");
if (message.containsKey("content")) {
return (String) message.get("content");
}
}
}
}
}
} catch (Exception e) {
log.warn("尝试格式1提取内容失败: ", e);
}
// 格式2: data.choices[0].content
try {
if (responseBody.containsKey("data")) {
Map<String, Object> data = (Map<String, Object>) responseBody.get("data");
if (data.containsKey("choices")) {
List<Map<String, Object>> choices = (List<Map<String, Object>>) data.get("choices");
if (choices != null && !choices.isEmpty()) {
Map<String, Object> choice = choices.get(0);
if (choice.containsKey("content")) {
return (String) choice.get("content");
}
}
}
}
} catch (Exception e) {
log.warn("尝试格式2提取内容失败: ", e);
}
// 格式3: result
try {
if (responseBody.containsKey("result")) {
return (String) responseBody.get("result");
}
} catch (Exception e) {
log.warn("尝试格式3提取内容失败: ", e);
}
// 记录完整响应,便于调试
log.info("响应体解析失败,完整响应: {}", responseBody);
return null;
}
}

View File

@@ -0,0 +1,134 @@
package com.example.service;
import cn.hutool.core.util.ObjectUtil;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.*;
import com.example.exception.CustomException;
import com.example.mapper.*;
import com.example.common.config.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class AdminService {
@Resource
private AdminMapper adminMapper;
/**
* 新增
*/
public void add(Admin admin) {
// 唯一校验
Admin dbAdmin = adminMapper.selectByUsername(admin.getUsername());
if (ObjectUtil.isNotNull(dbAdmin)) {
throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);
}
if (ObjectUtil.isEmpty(admin.getPassword())) {
admin.setPassword(Constants.USER_DEFAULT_PASSWORD);
}
if (ObjectUtil.isEmpty(admin.getName())) {
admin.setName(admin.getUsername());
}
admin.setRole("admin");
adminMapper.insert(admin);
}
/**
* 删除
*/
public void deleteById(Integer id) {
adminMapper.deleteById(id);
}
/**
* 批量删除
*/
public void deleteBatch(List<Integer> ids) {
for (Integer id : ids) {
adminMapper.deleteById(id);
}
}
/**
* 修改
*/
public void updateById(Admin admin) {
adminMapper.updateById(admin);
}
/**
* 根据ID查询
*/
public Admin selectById(Integer id) {
return adminMapper.selectById(id);
}
/**
* 查询所有
*/
public List<Admin> selectAll(Admin admin) {
return adminMapper.selectAll(admin);
}
/**
* 分页查询
*/
public PageInfo<Admin> selectPage(Admin admin, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Admin> list = adminMapper.selectAll(admin);
return PageInfo.of(list);
}
/**
* 注册
*/
public void register(Account account) {
Admin admin = new Admin();
admin.setUsername(account.getUsername());
admin.setPassword(account.getPassword());
this.add(admin);
}
public Admin login(Account account) {
Admin dbAdmin = adminMapper.selectByUsername(account.getUsername());
if (ObjectUtil.isNull(dbAdmin)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
if (!account.getPassword().equals(dbAdmin.getPassword())) {
throw new CustomException(ResultCodeEnum.USER_ACCOUNT_ERROR);
}
// 生成token
String tokenData = dbAdmin.getId() + "-admin";
String token = TokenUtils.genToken(tokenData, dbAdmin.getPassword());
dbAdmin.setToken(token);
return dbAdmin;
}
/**
* 修改密码
*/
public void updatePassword(Account account) {
Admin dbAdmin = adminMapper.selectByUsername(account.getUsername());
if (ObjectUtil.isNull(dbAdmin)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
if (!account.getPassword().equals(dbAdmin.getPassword())) {
throw new CustomException(ResultCodeEnum.PARAM_PASSWORD_ERROR);
}
dbAdmin.setPassword(account.getNewPassword());
adminMapper.updateById(dbAdmin);
}
}

View File

@@ -0,0 +1,164 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.Announcement;
import com.example.exception.CustomException;
import com.example.mapper.AnnouncementMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* 公告服务类
*/
@Service
public class AnnouncementService {
@Resource
private AnnouncementMapper announcementMapper;
/**
* 添加公告
* @param announcement 公告信息
*/
public void add(Announcement announcement) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有管理员可以发布公告");
}
// 设置默认值
if (announcement.getIsTop() == null) {
announcement.setIsTop(false);
}
if (announcement.getStatus() == null) {
announcement.setStatus(0); // 默认为草稿状态
}
announcement.setCreateTime(new Date());
announcement.setPublisherId(currentUser.getId());
announcement.setPublisherName(currentUser.getName());
announcementMapper.insert(announcement);
}
/**
* 更新公告
* @param announcement 公告信息
*/
public void update(Announcement announcement) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有管理员可以修改公告");
}
// 获取原公告
Announcement dbAnnouncement = announcementMapper.selectById(announcement.getId());
if (dbAnnouncement == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "公告不存在");
}
// 只更新部分字段,保留创建时间等信息
dbAnnouncement.setTitle(announcement.getTitle());
dbAnnouncement.setContent(announcement.getContent());
dbAnnouncement.setIsTop(announcement.getIsTop());
dbAnnouncement.setStatus(announcement.getStatus());
announcementMapper.updateById(dbAnnouncement);
}
/**
* 删除公告
* @param id 公告ID
*/
public void delete(Integer id) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有管理员可以删除公告");
}
announcementMapper.deleteById(id);
}
/**
* 获取公告详情
* @param id 公告ID
* @return 公告信息
*/
public Announcement getById(Integer id) {
return announcementMapper.selectById(id);
}
/**
* 获取所有已发布的公告(分页,所有用户可访问)
* @param pageNum 页码
* @param pageSize 每页大小
* @param title 标题关键字
* @return 分页公告列表
*/
public PageInfo<Announcement> getPublishedPage(Integer pageNum, Integer pageSize, String title) {
PageHelper.startPage(pageNum, pageSize);
List<Announcement> list = announcementMapper.selectAllPublishedWithKeyword(title);
return new PageInfo<>(list);
}
/**
* 获取所有公告(分页,包括草稿,仅管理员可用)
* @param pageNum 页码
* @param pageSize 每页大小
* @param title 标题关键字
* @return 分页公告列表
*/
public PageInfo<Announcement> getAllForAdmin(Integer pageNum, Integer pageSize, String title) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有管理员可以查看所有公告");
}
PageHelper.startPage(pageNum, pageSize);
List<Announcement> list = announcementMapper.selectAllForAdmin(title);
return new PageInfo<>(list);
}
/**
* 获取最新的几条已发布公告(用于首页展示)
* @param limit 限制条数
* @return 公告列表
*/
public List<Announcement> getLatestPublished(Integer limit) {
PageHelper.startPage(1, limit);
return announcementMapper.selectAllPublished();
}
}

View File

@@ -0,0 +1,530 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.config.TokenUtils;
import com.example.entity.Account;
import com.example.entity.AssessmentReport;
import com.example.entity.Patient;
import com.example.entity.SymptomRecord;
import com.example.exception.CustomException;
import com.example.mapper.AssessmentReportMapper;
import com.example.mapper.SymptomRecordMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.HashMap;
@Service
public class AssessmentReportService {
@Resource
private AssessmentReportMapper assessmentReportMapper;
@Resource
private SymptomRecordMapper symptomRecordMapper;
@Resource
private PatientService patientService;
/**
* 生成评估报告
* @param patientId 患者ID
* @param reportPeriod 报告周期类型("week"/"month"
* @return 生成的评估报告
*/
public AssessmentReport generateReport(Integer patientId, String reportPeriod) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
String role = currentUser.getRole();
if (!"admin".equals(role) && !"doctor".equals(role) &&
(!"patient".equals(role) || !currentUser.getId().equals(patientId))) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权生成该患者的评估报告");
}
// 设置报告时间范围
LocalDate endDate = LocalDate.now();
LocalDate startDate;
if ("week".equals(reportPeriod)) {
startDate = endDate.minusWeeks(1);
} else if ("month".equals(reportPeriod)) {
startDate = endDate.minusMonths(1);
} else {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无效的报告周期类型");
}
// 获取时间范围内的症状记录
List<SymptomRecord> records = symptomRecordMapper.selectByPatientIdAndDateRange(
patientId, startDate, endDate);
if (records.isEmpty()) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "所选时间范围内没有症状记录");
}
// 创建评估报告
AssessmentReport report = new AssessmentReport();
report.setPatientId(patientId);
report.setReportPeriod("week".equals(reportPeriod) ? "周报" : "月报");
report.setStartDate(startDate);
report.setEndDate(endDate);
report.setStatus("未读");
report.setCreateTime(LocalDateTime.now());
// 生成报告内容
generateReportContent(report, records);
// 保存报告
assessmentReportMapper.insert(report);
return report;
}
/**
* 生成报告内容
*/
private void generateReportContent(AssessmentReport report, List<SymptomRecord> records) {
// 计算统计数据
DoubleSummaryStatistics severityStats = records.stream()
.map(SymptomRecord::getSeverityLevel)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.summaryStatistics();
DoubleSummaryStatistics emotionStats = records.stream()
.map(SymptomRecord::getEmotionScore)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.summaryStatistics();
DoubleSummaryStatistics sleepStats = records.stream()
.map(SymptomRecord::getSleepTime)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.summaryStatistics();
// 生成总体评估
StringBuilder summary = new StringBuilder();
summary.append("")
.append(report.getReportPeriod())
.append("期间(")
.append(report.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE))
.append("")
.append(report.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE))
.append("),共记录了 ")
.append(records.size())
.append(" 条症状数据。\n\n");
summary.append("严重程度评分:平均 ")
.append(String.format("%.1f", severityStats.getAverage()))
.append(",最高 ")
.append((int)severityStats.getMax())
.append(",最低 ")
.append((int)severityStats.getMin())
.append("\n");
summary.append("情绪基线评分:平均 ")
.append(String.format("%.1f", emotionStats.getAverage()))
.append(",最高 ")
.append((int)emotionStats.getMax())
.append(",最低 ")
.append((int)emotionStats.getMin())
.append("\n");
summary.append("入睡时间:平均 ")
.append(String.format("%.1f", sleepStats.getAverage()))
.append(" 分钟,最长 ")
.append((int)sleepStats.getMax())
.append(" 分钟,最短 ")
.append((int)sleepStats.getMin())
.append(" 分钟。\n");
// 设置总体评估
report.setSummary(summary.toString());
// 生成趋势分析
StringBuilder trend = new StringBuilder();
// 按记录时间转换为LocalDate进行分组
Map<LocalDate, List<SymptomRecord>> recordsByDate = new HashMap<>();
for (SymptomRecord record : records) {
Date recordTime = record.getRecordTime();
if (recordTime != null) {
LocalDate recordDate = new java.sql.Date(recordTime.getTime()).toLocalDate();
if (!recordsByDate.containsKey(recordDate)) {
recordsByDate.put(recordDate, new ArrayList<>());
}
recordsByDate.get(recordDate).add(record);
}
}
// 获取排序后的日期列表
List<LocalDate> dates = new ArrayList<>(recordsByDate.keySet());
dates.sort(LocalDate::compareTo);
if (dates.size() > 1) {
// 计算首尾日期的平均严重程度
double firstDaySeverity = recordsByDate.get(dates.get(0)).stream()
.map(SymptomRecord::getSeverityLevel)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.average()
.orElse(0);
double lastDaySeverity = recordsByDate.get(dates.get(dates.size() - 1)).stream()
.map(SymptomRecord::getSeverityLevel)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.average()
.orElse(0);
// 趋势分析
trend.append("症状严重程度:");
if (lastDaySeverity < firstDaySeverity) {
trend.append("整体呈下降趋势,相比初始记录降低了 ")
.append(String.format("%.1f", firstDaySeverity - lastDaySeverity))
.append(" 点,病情有所好转。");
} else if (lastDaySeverity > firstDaySeverity) {
trend.append("整体呈上升趋势,相比初始记录增加了 ")
.append(String.format("%.1f", lastDaySeverity - firstDaySeverity))
.append(" 点,病情有所加重。");
} else {
trend.append("整体保持稳定,症状严重程度变化不明显。");
}
trend.append("\n\n");
// 情绪基线趋势分析
double firstDayEmotion = recordsByDate.get(dates.get(0)).stream()
.map(SymptomRecord::getEmotionScore)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.average()
.orElse(0);
double lastDayEmotion = recordsByDate.get(dates.get(dates.size() - 1)).stream()
.map(SymptomRecord::getEmotionScore)
.filter(val -> val != null)
.mapToDouble(Integer::doubleValue)
.average()
.orElse(0);
trend.append("情绪状态:");
if (lastDayEmotion > firstDayEmotion) {
trend.append("呈积极改善趋势,情绪基线提高了 ")
.append(String.format("%.1f", lastDayEmotion - firstDayEmotion))
.append(" 点。");
} else if (lastDayEmotion < firstDayEmotion) {
trend.append("呈下降趋势,情绪基线降低了 ")
.append(String.format("%.1f", firstDayEmotion - lastDayEmotion))
.append(" 点,需要关注情绪变化。");
} else {
trend.append("情绪基线保持稳定。");
}
} else {
trend.append("记录天数不足,无法进行趋势分析。建议持续记录症状,以便更准确地评估病情变化。");
}
// 设置趋势分析
report.setTrendAnalysis(trend.toString());
// 生成建议
StringBuilder recommendation = new StringBuilder();
// 基于严重程度的建议
double avgSeverity = severityStats.getAverage();
if (avgSeverity >= 7) {
recommendation.append("● 症状严重程度较高,建议及时就医或保持与医生的密切沟通。\n");
recommendation.append("● 注意休息,避免过度劳累和压力。\n");
} else if (avgSeverity >= 4) {
recommendation.append("● 症状处于中等程度,建议遵循医嘱进行调整。\n");
recommendation.append("● 可以尝试适当的放松训练和心理调适方法。\n");
} else {
recommendation.append("● 症状程度较轻,建议继续保持良好的生活习惯。\n");
}
// 基于情绪状态的建议
double avgEmotion = emotionStats.getAverage();
if (avgEmotion <= 4) {
recommendation.append("● 情绪基线较低,建议尝试以下方法改善情绪:\n");
recommendation.append(" - 每天进行至少30分钟的适当运动\n");
recommendation.append(" - 学习简单的冥想或深呼吸放松技巧\n");
recommendation.append(" - 与亲友多交流,分享感受\n");
}
// 基于睡眠情况的建议
double avgSleep = sleepStats.getAverage();
if (avgSleep > 30) {
recommendation.append("● 入睡时间较长,建议改善睡眠习惯:\n");
recommendation.append(" - 保持规律的睡眠时间表\n");
recommendation.append(" - 睡前一小时避免使用电子设备\n");
recommendation.append(" - 睡前可以喝杯温热的牛奶或进行轻度放松活动\n");
}
// 一般性建议
recommendation.append("\n持续记录症状变化这将帮助您和医生更好地了解病情发展制定更适合的治疗方案。");
// 设置建议
report.setRecommendation(recommendation.toString());
}
/**
* 每周一凌晨2点自动生成周报
*/
@Scheduled(cron = "0 0 2 ? * MON")
public void generateWeeklyReports() {
List<Account> patients = getPatients();
for (Account patient : patients) {
try {
generateReportForPatient(patient.getId(), "week");
} catch (Exception e) {
// 记录错误但不中断流程
System.err.println("为患者 " + patient.getId() + " 生成周报失败: " + e.getMessage());
}
}
}
/**
* 每月1日凌晨3点自动生成月报
*/
@Scheduled(cron = "0 0 3 1 * ?")
public void generateMonthlyReports() {
List<Account> patients = getPatients();
for (Account patient : patients) {
try {
generateReportForPatient(patient.getId(), "month");
} catch (Exception e) {
// 记录错误但不中断流程
System.err.println("为患者 " + patient.getId() + " 生成月报失败: " + e.getMessage());
}
}
}
/**
* 为患者生成报告(内部方法,用于定时任务)
*/
private AssessmentReport generateReportForPatient(Integer patientId, String reportPeriod) {
LocalDate endDate = LocalDate.now();
LocalDate startDate;
if ("week".equals(reportPeriod)) {
startDate = endDate.minusWeeks(1);
} else {
startDate = endDate.minusMonths(1);
}
// 获取时间范围内的症状记录
List<SymptomRecord> records = symptomRecordMapper.selectByPatientIdAndDateRange(
patientId, startDate, endDate);
// 如果没有记录,则不生成报告
if (records.isEmpty()) {
return null;
}
// 创建评估报告
AssessmentReport report = new AssessmentReport();
report.setPatientId(patientId);
report.setReportPeriod("week".equals(reportPeriod) ? "周报" : "月报");
report.setStartDate(startDate);
report.setEndDate(endDate);
report.setStatus("未读");
report.setCreateTime(LocalDateTime.now());
// 生成报告内容
generateReportContent(report, records);
// 保存报告
assessmentReportMapper.insert(report);
return report;
}
/**
* 获取所有患者列表
*/
private List<Account> getPatients() {
// 使用PatientService获取所有患者
Patient queryPatient = new Patient();
return new ArrayList<>(patientService.selectAll(queryPatient));
}
/**
* 获取患者的评估报告列表
*/
public List<AssessmentReport> getReportsByPatientId(Integer patientId) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
String role = currentUser.getRole();
if (!"admin".equals(role) && !"doctor".equals(role) &&
(!"patient".equals(role) || !currentUser.getId().equals(patientId))) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权查看该患者的评估报告");
}
return assessmentReportMapper.selectByPatientId(patientId);
}
/**
* 获取医生负责的患者的评估报告列表
*/
public List<AssessmentReport> getReportsByDoctorId(Integer doctorId) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
String role = currentUser.getRole();
if (!"admin".equals(role) &&
(!"doctor".equals(role) || !currentUser.getId().equals(doctorId))) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权查看该医生负责的评估报告");
}
return assessmentReportMapper.selectByDoctorId(doctorId);
}
/**
* 获取所有评估报告列表(管理员使用)
*/
public List<AssessmentReport> getAllReports() {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权查看所有评估报告");
}
return assessmentReportMapper.selectAllReports();
}
/**
* 添加医生批注
*/
public void addDoctorComment(Integer reportId, String comment) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 验证权限
if (!"doctor".equals(currentUser.getRole()) && !"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权添加批注");
}
// 获取报告
AssessmentReport report = assessmentReportMapper.selectById(reportId);
if (report == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "报告不存在");
}
// 更新批注
report.setDoctorComment(comment);
assessmentReportMapper.updateById(report);
}
/**
* 更新报告状态为已读
*/
public void markAsRead(Integer reportId) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 获取报告
AssessmentReport report = assessmentReportMapper.selectById(reportId);
if (report == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "报告不存在");
}
// 验证权限
if (!"admin".equals(currentUser.getRole()) &&
!"doctor".equals(currentUser.getRole()) &&
(!"patient".equals(currentUser.getRole()) || !currentUser.getId().equals(report.getPatientId()))) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权操作该报告");
}
// 更新状态
report.setStatus("已读");
assessmentReportMapper.updateById(report);
}
/**
* 通过ID获取单个报告
* @param reportId 报告ID
* @return 评估报告对象
*/
public AssessmentReport getReportById(Integer reportId) {
if (reportId == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "报告ID不能为空");
}
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 查询报告
AssessmentReport report = assessmentReportMapper.selectById(reportId);
if (report == null) {
return null;
}
// 权限检查
String role = currentUser.getRole();
Integer userId = currentUser.getId();
// 管理员可以查看所有报告
if ("admin".equals(role)) {
return report;
}
// 医生可以查看自己负责的患者的报告
if ("doctor".equals(role)) {
List<AssessmentReport> doctorReports = getReportsByDoctorId(userId);
boolean hasAccess = doctorReports.stream()
.anyMatch(r -> r.getId().equals(reportId));
if (hasAccess) {
return report;
}
}
// 患者只能查看自己的报告
if ("patient".equals(role) && userId.equals(report.getPatientId())) {
return report;
}
// 无权限访问
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权访问该报告");
}
}

View File

@@ -0,0 +1,221 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.ChatMessage;
import com.example.entity.Doctor;
import com.example.entity.DoctorPatient;
import com.example.entity.Patient;
import com.example.exception.CustomException;
import com.example.mapper.ChatMessageMapper;
import com.example.mapper.DoctorMapper;
import com.example.mapper.PatientMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ChatMessageService {
@Resource
private ChatMessageMapper chatMessageMapper;
@Resource
private DoctorPatientService doctorPatientService;
@Resource
private DoctorMapper doctorMapper;
@Resource
private PatientMapper patientMapper;
/**
* 发送消息 - 通过HTTP接口调用
*/
public ChatMessage sendMessage(ChatMessage chatMessage) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 根据当前用户角色设置发送者信息
chatMessage.setSenderId(currentUser.getId());
chatMessage.setSenderType(currentUser.getRole());
// 检查发送者和接收者是否有关联关系
checkRelationship(chatMessage.getSenderId(), chatMessage.getSenderType(),
chatMessage.getReceiverId(), chatMessage.getReceiverType());
// 设置消息状态
chatMessage.setSendTime(new Date());
chatMessage.setIsRead(false);
// 保存消息
chatMessageMapper.insert(chatMessage);
return chatMessage;
}
/**
* 直接保存消息 - 通过WebSocket调用不依赖TokenUtils
*/
public ChatMessage directSaveMessage(ChatMessage chatMessage) {
// 检查发送者和接收者是否有关联关系
checkRelationship(chatMessage.getSenderId(), chatMessage.getSenderType(),
chatMessage.getReceiverId(), chatMessage.getReceiverType());
// 保存消息
chatMessageMapper.insert(chatMessage);
return chatMessage;
}
/**
* 获取聊天历史记录
*/
public List<ChatMessage> getChatHistory(Integer contactId, String contactType) {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 检查关系
checkRelationship(currentUser.getId(), currentUser.getRole(), contactId, contactType);
// 查询聊天记录
List<ChatMessage> messages = chatMessageMapper.selectChatHistory(
currentUser.getId(), currentUser.getRole(), contactId, contactType);
// 将对方发送的未读消息标记为已读
chatMessageMapper.markAsRead(currentUser.getId(), currentUser.getRole(), contactId, contactType);
// 填充发送者和接收者的名称
fillUserInfo(messages);
return messages;
}
/**
* 获取联系人列表
*/
public List<Map<String, Object>> getContactList() {
// 获取当前用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
List<Map<String, Object>> result = new ArrayList<>();
// 根据用户角色获取联系人
if ("doctor".equals(currentUser.getRole())) {
// 医生获取其患者列表
List<DoctorPatient> relations = doctorPatientService.selectByDoctorId(currentUser.getId(), null);
for (DoctorPatient relation : relations) {
if ("正常".equals(relation.getStatus()) && relation.getPatient() != null) {
Map<String, Object> contact = new HashMap<>();
contact.put("contactId", relation.getPatientId());
contact.put("contactType", "patient");
contact.put("contactName", relation.getPatient().getName());
contact.put("contactAvatar", relation.getPatient().getAvatar());
// 查询未读消息数
int unread = chatMessageMapper.countUnreadMessages(
currentUser.getId(), currentUser.getRole(), relation.getPatientId(), "patient");
contact.put("unreadCount", unread);
// 查询最新一条消息
List<ChatMessage> lastMessages = chatMessageMapper.selectChatHistory(
currentUser.getId(), currentUser.getRole(), relation.getPatientId(), "patient");
if (!lastMessages.isEmpty()) {
contact.put("lastMessage", lastMessages.get(lastMessages.size() - 1));
}
result.add(contact);
}
}
} else if ("patient".equals(currentUser.getRole())) {
// 患者获取其医生列表
List<DoctorPatient> relations = doctorPatientService.selectByPatientId(currentUser.getId(), null);
for (DoctorPatient relation : relations) {
if ("正常".equals(relation.getStatus()) && relation.getDoctor() != null) {
Map<String, Object> contact = new HashMap<>();
contact.put("contactId", relation.getDoctorId());
contact.put("contactType", "doctor");
contact.put("contactName", relation.getDoctor().getName());
contact.put("contactAvatar", relation.getDoctor().getAvatar());
// 查询未读消息数
int unread = chatMessageMapper.countUnreadMessages(
currentUser.getId(), currentUser.getRole(), relation.getDoctorId(), "doctor");
contact.put("unreadCount", unread);
// 查询最新一条消息
List<ChatMessage> lastMessages = chatMessageMapper.selectChatHistory(
currentUser.getId(), currentUser.getRole(), relation.getDoctorId(), "doctor");
if (!lastMessages.isEmpty()) {
contact.put("lastMessage", lastMessages.get(lastMessages.size() - 1));
}
result.add(contact);
}
}
}
return result;
}
/**
* 检查用户关系
*/
private void checkRelationship(Integer senderId, String senderType, Integer receiverId, String receiverType) {
// 检查医生和患者是否存在关联关系
if ("doctor".equals(senderType) && "patient".equals(receiverType)) {
List<DoctorPatient> relations = doctorPatientService.selectByDoctorId(senderId, null);
boolean hasRelation = relations.stream()
.anyMatch(dp -> dp.getPatientId().equals(receiverId) && "正常".equals(dp.getStatus()));
if (!hasRelation) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "您与该患者没有关联关系");
}
} else if ("patient".equals(senderType) && "doctor".equals(receiverType)) {
List<DoctorPatient> relations = doctorPatientService.selectByPatientId(senderId, null);
boolean hasRelation = relations.stream()
.anyMatch(dp -> dp.getDoctorId().equals(receiverId) && "正常".equals(dp.getStatus()));
if (!hasRelation) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "您与该医生没有关联关系");
}
} else {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无效的聊天关系");
}
}
/**
* 填充用户信息
*/
private void fillUserInfo(List<ChatMessage> messages) {
for (ChatMessage message : messages) {
// 填充发送者信息
if ("doctor".equals(message.getSenderType())) {
Doctor doctor = doctorMapper.selectById(message.getSenderId());
if (doctor != null) {
message.setSenderName(doctor.getName());
message.setSenderAvatar(doctor.getAvatar());
}
} else if ("patient".equals(message.getSenderType())) {
Patient patient = patientMapper.selectById(message.getSenderId());
if (patient != null) {
message.setSenderName(patient.getName());
message.setSenderAvatar(patient.getAvatar());
}
}
// 可以根据需要填充接收者信息
}
}
}

View File

@@ -0,0 +1,121 @@
package com.example.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.Result;
import com.example.entity.Doctor;
import com.example.entity.DoctorPatient;
import com.example.entity.Patient;
import com.example.mapper.DoctorMapper;
import com.example.mapper.DoctorPatientMapper;
import com.example.mapper.PatientMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Service
public class DoctorPatientService {
@Resource
private DoctorPatientMapper doctorPatientMapper;
@Resource
private DoctorMapper doctorMapper;
@Resource
private PatientMapper patientMapper;
/**
* 新增医生患者关系
* @param doctorPatient 关系信息
*/
public void add(DoctorPatient doctorPatient) {
// 检查医生ID是否存在
Doctor doctor = doctorMapper.selectById(doctorPatient.getDoctorId());
if (ObjectUtil.isEmpty(doctor)) {
throw new RuntimeException("医生不存在");
}
// 检查患者ID是否存在
Patient patient = patientMapper.selectById(doctorPatient.getPatientId());
if (ObjectUtil.isEmpty(patient)) {
throw new RuntimeException("患者不存在");
}
// 检查关系是否已存在
LambdaQueryWrapper<DoctorPatient> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DoctorPatient::getDoctorId, doctorPatient.getDoctorId())
.eq(DoctorPatient::getPatientId, doctorPatient.getPatientId())
.eq(DoctorPatient::getStatus, "正常");
if (doctorPatientMapper.selectCount(queryWrapper) > 0) {
throw new RuntimeException("医生和患者关系已存在");
}
// 设置默认值
doctorPatient.setRelationshipStart(new Date());
doctorPatient.setStatus("正常");
doctorPatient.setCreateTime(new Date());
doctorPatientMapper.insert(doctorPatient);
}
/**
* 更新医生患者关系
* @param doctorPatient 关系信息
*/
public void update(DoctorPatient doctorPatient) {
DoctorPatient dbDoctorPatient = doctorPatientMapper.selectById(doctorPatient.getId());
if (ObjectUtil.isEmpty(dbDoctorPatient)) {
throw new RuntimeException("关系不存在");
}
doctorPatientMapper.updateById(doctorPatient);
}
/**
* 删除医生患者关系
* @param id 关系ID
*/
public void delete(Integer id) {
doctorPatientMapper.deleteById(id);
}
/**
* 结束医生患者关系
* @param id 关系ID
*/
public void endRelationship(Integer id) {
DoctorPatient doctorPatient = doctorPatientMapper.selectById(id);
if (ObjectUtil.isEmpty(doctorPatient)) {
throw new RuntimeException("关系不存在");
}
doctorPatient.setStatus("已结束");
doctorPatientMapper.updateById(doctorPatient);
}
/**
* 根据医生ID查询患者
* @param doctorId 医生ID
* @param name 患者姓名(可选)
* @return 患者列表
*/
public List<DoctorPatient> selectByDoctorId(Integer doctorId, String name) {
return doctorPatientMapper.selectByDoctorId(doctorId, name);
}
/**
* 根据患者ID查询医生
* @param patientId 患者ID
* @param name 医生姓名(可选)
* @return 医生列表
*/
public List<DoctorPatient> selectByPatientId(Integer patientId, String name) {
return doctorPatientMapper.selectByPatientId(patientId, name);
}
}

View File

@@ -0,0 +1,132 @@
package com.example.service;
import cn.hutool.core.util.ObjectUtil;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.*;
import com.example.exception.CustomException;
import com.example.mapper.*;
import com.example.common.config.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class DoctorService {
@Resource
private DoctorMapper doctorMapper;
/**
* 新增
*/
public void add(Doctor doctor) {
// 唯一校验
Doctor dbDoctor = doctorMapper.selectByUsername(doctor.getUsername());
if (ObjectUtil.isNotNull(dbDoctor)) {
throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);
}
if (ObjectUtil.isEmpty(doctor.getPassword())) {
doctor.setPassword(Constants.USER_DEFAULT_PASSWORD);
}
if (ObjectUtil.isEmpty(doctor.getName())) {
doctor.setName(doctor.getUsername());
}
doctor.setRole("doctor");
doctorMapper.insert(doctor);
}
/**
* 删除
*/
public void deleteById(Integer id) {
doctorMapper.deleteById(id);
}
/**
* 批量删除
*/
public void deleteBatch(List<Integer> ids) {
for (Integer id : ids) {
doctorMapper.deleteById(id);
}
}
/**
* 修改
*/
public void updateById(Doctor doctor) {
doctorMapper.updateById(doctor);
}
/**
* 根据ID查询
*/
public Doctor selectById(Integer id) {
return doctorMapper.selectById(id);
}
/**
* 查询所有
*/
public List<Doctor> selectAll(Doctor doctor) {
return doctorMapper.selectAll(doctor);
}
/**
* 分页查询
*/
public PageInfo<Doctor> selectPage(Doctor doctor, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Doctor> list = doctorMapper.selectAll(doctor);
return PageInfo.of(list);
}
/**
* 注册
*/
public void register(Account account) {
Doctor doctor = new Doctor();
doctor.setUsername(account.getUsername());
doctor.setPassword(account.getPassword());
doctor.setSex(account.getSex());
doctor.setName(account.getName());
doctor.setPhone(account.getPhone());
this.add(doctor);
}
public Doctor login(Account account) {
Doctor dbDoctor = doctorMapper.selectByUsername(account.getUsername());
if (ObjectUtil.isNull(dbDoctor)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
if (!account.getPassword().equals(dbDoctor.getPassword())) {
throw new CustomException(ResultCodeEnum.USER_ACCOUNT_ERROR);
}
// 生成token
String tokenData = dbDoctor.getId() + "-doctor";
String token = TokenUtils.genToken(tokenData, dbDoctor.getPassword());
dbDoctor.setToken(token);
return dbDoctor;
}
/**
* 修改密码
*/
public void updatePassword(Account account) {
Doctor dbDoctor = doctorMapper.selectByUsername(account.getUsername());
if (ObjectUtil.isNull(dbDoctor)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
if (!account.getPassword().equals(dbDoctor.getPassword())) {
throw new CustomException(ResultCodeEnum.PARAM_PASSWORD_ERROR);
}
dbDoctor.setPassword(account.getNewPassword());
doctorMapper.updateById(dbDoctor);
}
}

View File

@@ -0,0 +1,200 @@
package com.example.service;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.*;
import com.example.exception.CustomException;
import com.example.mapper.*;
import com.example.common.config.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Service
public class PatientService {
@Resource
private PatientMapper patientMapper;
@Resource
private DoctorPatientMapper doctorPatientMapper;
/**
* 新增
*/
public void add(Patient patient) {
// 校验数据
if (ObjectUtil.isEmpty(patient.getUsername()) || ObjectUtil.isEmpty(patient.getPassword())) {
throw new CustomException(ResultCodeEnum.PARAM_LOST_ERROR);
}
// 检查用户名是否存在
Patient dbPatient = selectByUsername(patient.getUsername());
if (dbPatient != null) {
throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);
}
// 设置默认值
if (ObjectUtil.isEmpty(patient.getName())) {
patient.setName(patient.getUsername());
}
patient.setRole("patient");
patientMapper.insert(patient);
}
/**
* 通过ID删除
*/
public void deleteById(Integer id) {
patientMapper.deleteById(id);
}
/**
* 批量删除
*/
public void deleteBatch(List<Integer> ids) {
for (Integer id : ids) {
patientMapper.deleteById(id);
}
}
/**
* 更新
*/
public void updateById(Patient patient) {
patientMapper.updateById(patient);
}
/**
* 通过ID查询
*/
public Patient selectById(Integer id) {
return patientMapper.selectById(id);
}
/**
* 通过用户名查询
*/
public Patient selectByUsername(String username) {
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Patient::getUsername, username);
return patientMapper.selectOne(queryWrapper);
}
/**
* 查询所有
*/
public List<Patient> selectAll(Patient patient) {
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
return patientMapper.selectList(queryWrapper);
}
/**
* 分页查询 - 按患者信息查询
*/
public Page<Patient> selectPage(Patient patient, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
if (ObjectUtil.isNotEmpty(patient.getName())) {
queryWrapper.like(Patient::getName, patient.getName());
}
return patientMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
}
/**
* 分页查询 - 按患者姓名查询
*/
public Page<Patient> selectPage(Integer pageNum, Integer pageSize, String name) {
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
if (ObjectUtil.isNotEmpty(name)) {
queryWrapper.like(Patient::getName, name);
}
return patientMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
}
/**
* 用户登录
*/
public Account login(Account account) {
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Patient::getUsername, account.getUsername());
queryWrapper.eq(Patient::getPassword, account.getPassword());
Patient patient = patientMapper.selectOne(queryWrapper);
if (ObjectUtil.isEmpty(patient)) {
throw new CustomException(ResultCodeEnum.USER_ACCOUNT_ERROR);
}
// 生成token
String tokenData = patient.getId() + "-" + patient.getRole();
String token = TokenUtils.genToken(tokenData, patient.getPassword());
patient.setToken(token);
return patient;
}
/**
* 用户注册
*/
public void register(Account account) {
// 检查用户名是否已存在
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Patient::getUsername, account.getUsername());
if (patientMapper.selectCount(queryWrapper) > 0) {
throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);
}
Patient patient = new Patient();
patient.setUsername(account.getUsername());
patient.setPassword(account.getPassword());
patient.setName(account.getName());
patient.setSex(account.getSex());
patient.setPhone(account.getPhone());
patient.setEmail(account.getEmail());
patient.setRole("patient");
add(patient);
// 如果选择了医生,则建立医患关系
if (account.getDoctorId() != null) {
DoctorPatient doctorPatient = new DoctorPatient();
doctorPatient.setDoctorId(account.getDoctorId());
doctorPatient.setPatientId(patient.getId());
doctorPatient.setRelationshipStart(new Date());
doctorPatient.setStatus("正常");
doctorPatient.setCreateTime(new Date());
doctorPatientMapper.insert(doctorPatient);
}
}
/**
* 修改密码
*/
public void updatePassword(Account account) {
Patient dbPatient = selectById(account.getId());
if (ObjectUtil.isEmpty(dbPatient)) {
throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
}
if (!account.getPassword().equals(dbPatient.getPassword())) {
throw new CustomException(ResultCodeEnum.PARAM_PASSWORD_ERROR);
}
Patient patient = new Patient();
patient.setId(account.getId());
patient.setPassword(account.getNewPassword());
updateById(patient);
}
}

View File

@@ -0,0 +1,385 @@
package com.example.service;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.ResourceRating;
import com.example.entity.TreatmentResource;
import com.example.exception.CustomException;
import com.example.mapper.ResourceCategoryMapper;
import com.example.mapper.ResourceRatingMapper;
import com.example.mapper.TreatmentResourceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 基于协同过滤的推荐服务
*/
@Service
public class RecommendationService {
@Resource
private ResourceRatingMapper resourceRatingMapper;
@Resource
private TreatmentResourceMapper treatmentResourceMapper;
@Resource
private ResourceCategoryMapper resourceCategoryMapper;
/**
* 获取针对特定用户的资源推荐
* @param userId 用户ID
* @param limit 推荐数量限制
* @return 推荐资源列表
*/
public List<Map<String, Object>> getRecommendationsForUser(Integer userId, Integer limit) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null || !userId.equals(currentUser.getId())) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 只有患者才能获取推荐
if (!"patient".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有患者才能获取推荐");
}
// 获取所有评分数据
List<ResourceRating> allRatings = resourceRatingMapper.selectList(null);
// 获取目标用户的评分记录
List<ResourceRating> userRatings = allRatings.stream()
.filter(r -> r.getPatientId().equals(userId))
.collect(Collectors.toList());
// 如果用户没有评分记录,返回热门资源
if (userRatings.isEmpty()) {
return getPopularResources(limit);
}
// 计算用户相似度
List<Map<String, Object>> userSimilarities = calculateUserSimilarity(userId, allRatings);
// 获取用户已评分的资源ID
Set<Integer> userRatedResourceIds = userRatings.stream()
.map(ResourceRating::getResourceId)
.collect(Collectors.toSet());
// 获取所有已审核通过的资源
List<TreatmentResource> allResources = treatmentResourceMapper.selectAllAudited("");
// 获取用户未评分的资源
List<TreatmentResource> unratedResources = allResources.stream()
.filter(resource -> !userRatedResourceIds.contains(resource.getId()))
.collect(Collectors.toList());
// 对每个未评分资源预测评分并生成推荐
List<Map<String, Object>> recommendationsWithScores = new ArrayList<>();
for (TreatmentResource resource : unratedResources) {
double predictedRating = predictRating(resource.getId(), userSimilarities, allRatings);
// 只推荐预测评分大于0的资源
if (predictedRating > 0) {
Map<String, Object> recommendation = new HashMap<>();
recommendation.put("resource", resource);
recommendation.put("predictedRating", predictedRating);
recommendation.put("similarityScore", Math.min(100, predictedRating * 20)); // 将5分制转换为100分制
recommendation.put("reason", getRecommendationReason(resource, userSimilarities, allRatings, userRatings, allResources));
// 添加分类名称
com.example.entity.ResourceCategory category = resourceCategoryMapper.selectById(resource.getCategoryId());
if (category != null) {
recommendation.put("categoryName", category.getName());
}
recommendationsWithScores.add(recommendation);
}
}
// 按预测评分降序排序,取前几个作为推荐
recommendationsWithScores.sort((a, b) ->
Double.compare((Double) b.get("predictedRating"), (Double) a.get("predictedRating")));
// 限制推荐数量
int resultLimit = Math.min(limit, recommendationsWithScores.size());
return recommendationsWithScores.subList(0, resultLimit);
}
/**
* 获取热门资源作为推荐(用于冷启动问题)
*/
private List<Map<String, Object>> getPopularResources(Integer limit) {
// 获取所有已审核通过的资源
List<TreatmentResource> allResources = treatmentResourceMapper.selectAllAudited("");
// 按照访问次数和下载次数之和排序
allResources.sort((a, b) -> {
int aPopularity = (a.getVisitCount() != null ? a.getVisitCount() : 0) +
(a.getDownloadCount() != null ? a.getDownloadCount() : 0);
int bPopularity = (b.getVisitCount() != null ? b.getVisitCount() : 0) +
(b.getDownloadCount() != null ? b.getDownloadCount() : 0);
return Integer.compare(bPopularity, aPopularity);
});
// 转换为推荐格式
List<Map<String, Object>> recommendations = new ArrayList<>();
int resultLimit = Math.min(limit, allResources.size());
for (int i = 0; i < resultLimit; i++) {
TreatmentResource resource = allResources.get(i);
Map<String, Object> recommendation = new HashMap<>();
recommendation.put("resource", resource);
recommendation.put("predictedRating", 4.0); // 默认预测评分
recommendation.put("similarityScore", 80.0); // 默认相似度分数
recommendation.put("reason", "系统热门资源推荐");
// 添加分类名称
com.example.entity.ResourceCategory category = resourceCategoryMapper.selectById(resource.getCategoryId());
if (category != null) {
recommendation.put("categoryName", category.getName());
}
recommendations.add(recommendation);
}
return recommendations;
}
/**
* 计算用户相似度 - 使用余弦相似度
*/
private List<Map<String, Object>> calculateUserSimilarity(Integer targetUserId, List<ResourceRating> allRatings) {
// 获取目标用户的评分记录
List<ResourceRating> targetUserRatings = allRatings.stream()
.filter(r -> r.getPatientId().equals(targetUserId))
.collect(Collectors.toList());
if (targetUserRatings.isEmpty()) {
return Collections.emptyList();
}
// 用户ID列表去除目标用户
List<Integer> userIds = allRatings.stream()
.map(ResourceRating::getPatientId)
.distinct()
.filter(id -> !id.equals(targetUserId))
.collect(Collectors.toList());
// 计算每个用户与目标用户的相似度
List<Map<String, Object>> similarities = new ArrayList<>();
for (Integer userId : userIds) {
// 获取当前用户的评分记录
List<ResourceRating> userRatings = allRatings.stream()
.filter(r -> r.getPatientId().equals(userId))
.collect(Collectors.toList());
if (userRatings.isEmpty()) {
continue;
}
// 找出两个用户共同评分的资源
Set<Integer> targetResourceIds = targetUserRatings.stream()
.map(ResourceRating::getResourceId)
.collect(Collectors.toSet());
Set<Integer> userResourceIds = userRatings.stream()
.map(ResourceRating::getResourceId)
.collect(Collectors.toSet());
Set<Integer> commonResourceIds = new HashSet<>(targetResourceIds);
commonResourceIds.retainAll(userResourceIds);
// 如果没有共同评分的资源相似度为0
if (commonResourceIds.isEmpty()) {
continue;
}
// 提取两个用户对共同资源的评分向量
double[] targetVector = new double[commonResourceIds.size()];
double[] userVector = new double[commonResourceIds.size()];
int index = 0;
for (Integer resourceId : commonResourceIds) {
double targetRating = targetUserRatings.stream()
.filter(r -> r.getResourceId().equals(resourceId))
.findFirst()
.map(r -> (double) r.getRating())
.orElse(0.0);
double userRating = userRatings.stream()
.filter(r -> r.getResourceId().equals(resourceId))
.findFirst()
.map(r -> (double) r.getRating())
.orElse(0.0);
targetVector[index] = targetRating;
userVector[index] = userRating;
index++;
}
// 计算余弦相似度
// 余弦相似度 = (A·B) / (||A|| * ||B||)
double dotProduct = 0.0;
double targetMagnitude = 0.0;
double userMagnitude = 0.0;
for (int i = 0; i < targetVector.length; i++) {
dotProduct += targetVector[i] * userVector[i];
targetMagnitude += targetVector[i] * targetVector[i];
userMagnitude += userVector[i] * userVector[i];
}
targetMagnitude = Math.sqrt(targetMagnitude);
userMagnitude = Math.sqrt(userMagnitude);
// 避免除以零
double similarity = (targetMagnitude > 0 && userMagnitude > 0)
? dotProduct / (targetMagnitude * userMagnitude)
: 0;
Map<String, Object> userSimilarity = new HashMap<>();
userSimilarity.put("userId", userId);
userSimilarity.put("similarity", similarity);
similarities.add(userSimilarity);
}
// 按相似度降序排序
similarities.sort((a, b) -> Double.compare(
(Double) b.get("similarity"),
(Double) a.get("similarity")
));
return similarities;
}
/**
* 预测用户对资源的评分
*/
private double predictRating(Integer resourceId, List<Map<String, Object>> userSimilarities,
List<ResourceRating> allRatings) {
// 获取已对该资源评分的相似用户
List<Map<String, Object>> similarUsersWithRating = userSimilarities.stream()
.filter(s -> allRatings.stream()
.anyMatch(r -> r.getPatientId().equals(s.get("userId")) &&
r.getResourceId().equals(resourceId)))
.collect(Collectors.toList());
// 如果没有相似用户对该资源评分,返回默认预测值
if (similarUsersWithRating.isEmpty()) {
return 0;
}
// 计算加权评分和
double weightedSum = 0.0;
double similaritySum = 0.0;
for (Map<String, Object> user : similarUsersWithRating) {
Integer userId = (Integer) user.get("userId");
double similarity = (Double) user.get("similarity");
// 找到该用户对该资源的评分
double rating = allRatings.stream()
.filter(r -> r.getPatientId().equals(userId) && r.getResourceId().equals(resourceId))
.findFirst()
.map(r -> (double) r.getRating())
.orElse(0.0);
// 加权和
weightedSum += similarity * rating;
similaritySum += similarity;
}
// 计算加权平均
return similaritySum > 0 ? weightedSum / similaritySum : 0;
}
/**
* 获取推荐理由
*/
private String getRecommendationReason(TreatmentResource resource,
List<Map<String, Object>> userSimilarities,
List<ResourceRating> allRatings,
List<ResourceRating> userRatings,
List<TreatmentResource> allResources) {
// 找出评分最高的相似用户对该资源的评价
List<Map<String, Object>> topSimilarUsers = userSimilarities.stream()
.limit(3)
.collect(Collectors.toList());
// 找出这些用户中有对该资源评分的用户
List<Map<String, Object>> usersWithRating = topSimilarUsers.stream()
.filter(s -> allRatings.stream()
.anyMatch(r -> r.getPatientId().equals(s.get("userId")) &&
r.getResourceId().equals(resource.getId())))
.collect(Collectors.toList());
if (usersWithRating.isEmpty()) {
// 基于内容的推荐理由
if (!userRatings.isEmpty()) {
// 查找用户评分最高的资源
ResourceRating topRatedRating = userRatings.stream()
.max(Comparator.comparing(ResourceRating::getRating))
.orElse(null);
if (topRatedRating != null) {
// 获取用户最喜欢的资源详情
Integer topRatedResourceId = topRatedRating.getResourceId();
TreatmentResource topRatedResource = allResources.stream()
.filter(r -> r.getId().equals(topRatedResourceId))
.findFirst()
.orElse(null);
// 如果当前资源与用户最喜欢的资源属于同一分类
if (topRatedResource != null && resource.getCategoryId().equals(topRatedResource.getCategoryId())) {
return "根据您对同类资源的偏好推荐";
}
// 如果当前资源适用的症状与用户评分高的资源相似
List<String> userPreferredSymptoms = userRatings.stream()
.filter(r -> r.getRating() >= 4)
.map(r -> {
// 获取资源详情
Optional<TreatmentResource> res = allResources.stream()
.filter(tr -> tr.getId().equals(r.getResourceId()))
.findFirst();
return res.map(TreatmentResource::getApplicableSymptoms).orElse("");
})
.flatMap(s -> Arrays.stream(s.split("[,、]")))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
List<String> resourceSymptoms = Arrays.stream(
resource.getApplicableSymptoms() != null
? resource.getApplicableSymptoms().split("[,、]")
: new String[0])
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
boolean hasCommonSymptoms = resourceSymptoms.stream()
.anyMatch(userPreferredSymptoms::contains);
if (hasCommonSymptoms) {
return "包含您可能关注的症状";
}
}
}
return "基于系统热门资源推荐";
}
// 基于相似用户的推荐理由
if (usersWithRating.size() == 1) {
return "与您兴趣相似的用户喜欢此资源";
}
return usersWithRating.size() + "位与您兴趣相似的用户喜欢此资源";
}
}

View File

@@ -0,0 +1,88 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.ResourceCategory;
import com.example.entity.TreatmentResource;
import com.example.exception.CustomException;
import com.example.mapper.ResourceCategoryMapper;
import com.example.mapper.TreatmentResourceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import java.util.List;
@Service
public class ResourceCategoryService {
@Resource
private ResourceCategoryMapper resourceCategoryMapper;
@Resource
private TreatmentResourceMapper treatmentResourceMapper;
public List<ResourceCategory> findAll() {
return resourceCategoryMapper.selectAllCategories();
}
public List<ResourceCategory> findByParentId(Integer parentId) {
return resourceCategoryMapper.selectByParentId(parentId);
}
public ResourceCategory findById(Integer id) {
return resourceCategoryMapper.selectById(id);
}
public void add(ResourceCategory category) {
// 检查是否存在同名分类
LambdaQueryWrapper<ResourceCategory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ResourceCategory::getName, category.getName())
.eq(ResourceCategory::getParentId, category.getParentId());
if (resourceCategoryMapper.selectCount(wrapper) > 0) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "已存在同名分类");
}
resourceCategoryMapper.insert(category);
}
public void update(ResourceCategory category) {
// 检查是否存在同名分类(排除自身)
LambdaQueryWrapper<ResourceCategory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ResourceCategory::getName, category.getName())
.eq(ResourceCategory::getParentId, category.getParentId())
.ne(ResourceCategory::getId, category.getId());
if (resourceCategoryMapper.selectCount(wrapper) > 0) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "已存在同名分类");
}
resourceCategoryMapper.updateById(category);
}
public void delete(Integer id) {
// 检查是否有子分类
LambdaQueryWrapper<ResourceCategory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ResourceCategory::getParentId, id);
if (resourceCategoryMapper.selectCount(wrapper) > 0) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "该分类下存在子分类,无法删除");
}
// 检查是否有关联的资源
List<TreatmentResource> resources = treatmentResourceMapper.selectByCategoryId(id);
if (resources != null && !resources.isEmpty()) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "该分类下存在资源,无法删除");
}
resourceCategoryMapper.deleteById(id);
}
public PageInfo<ResourceCategory> selectPage(ResourceCategory category, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<ResourceCategory> list = resourceCategoryMapper.selectPage(category.getName() != null ? category.getName() : "");
return new PageInfo<>(list);
}
}

View File

@@ -0,0 +1,161 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.ResourceRating;
import com.example.entity.TreatmentResource;
import com.example.exception.CustomException;
import com.example.mapper.ResourceRatingMapper;
import com.example.mapper.TreatmentResourceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class ResourceRatingService {
@Resource
private ResourceRatingMapper resourceRatingMapper;
@Resource
private TreatmentResourceMapper treatmentResourceMapper;
/**
* 添加评分
*/
public void add(ResourceRating resourceRating) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 只有患者可以添加评分
if (!"patient".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有患者可以评分");
}
// 设置患者ID
resourceRating.setPatientId(currentUser.getId());
// 检查评分是否有效
if (resourceRating.getRating() == null || resourceRating.getRating() < 1 || resourceRating.getRating() > 5) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "评分必须在1-5之间");
}
// 检查是否已经评分过
int count = resourceRatingMapper.countByResourceIdAndPatientId(resourceRating.getResourceId(), currentUser.getId());
if (count > 0) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "您已经评价过该资源");
}
// 设置创建时间
resourceRating.setCreateTime(LocalDateTime.now());
// 保存评分
resourceRatingMapper.insert(resourceRating);
}
/**
* 根据资源ID查询评分记录
*/
public List<ResourceRating> findByResourceId(Integer resourceId) {
return resourceRatingMapper.selectByResourceId(resourceId);
}
/**
* 获取资源的平均评分
*/
public double getAverageRating(Integer resourceId) {
return resourceRatingMapper.getAverageRating(resourceId);
}
public Integer getRatingCount(Integer resourceId) {
return resourceRatingMapper.getRatingCount(resourceId);
}
public void rateResource(ResourceRating rating) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 只有患者才能评价资源
if (!"patient".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "只有患者才能评价资源");
}
// 检查资源是否存在
TreatmentResource resource = treatmentResourceMapper.selectById(rating.getResourceId());
if (resource == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "资源不存在");
}
// 检查资源是否已通过审核
if (!"已通过".equals(resource.getAuditStatus())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "资源未通过审核,无法评价");
}
// 检查患者是否已经评价过该资源
ResourceRating existingRating = resourceRatingMapper.findByResourceAndPatient(
rating.getResourceId(), currentUser.getId());
if (existingRating != null) {
// 更新评价
existingRating.setRating(rating.getRating());
existingRating.setComment(rating.getComment());
resourceRatingMapper.updateById(existingRating);
} else {
// 新增评价
rating.setPatientId(currentUser.getId());
rating.setCreateTime(LocalDateTime.now());
resourceRatingMapper.insert(rating);
}
}
public void deleteRating(Integer id) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 查询评价
ResourceRating rating = resourceRatingMapper.selectById(id);
if (rating == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "评价不存在");
}
// 只有评价的患者本人或管理员才能删除评价
if (!currentUser.getId().equals(rating.getPatientId()) && !"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权限删除该评价");
}
resourceRatingMapper.deleteById(id);
}
/**
* 根据患者ID查询评分记录
*/
public List<ResourceRating> findByPatientId(Integer patientId) {
LambdaQueryWrapper<ResourceRating> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ResourceRating::getPatientId, patientId);
queryWrapper.orderByDesc(ResourceRating::getCreateTime);
return resourceRatingMapper.selectList(queryWrapper);
}
/**
* 查询所有评分记录
*/
public List<ResourceRating> findAll() {
LambdaQueryWrapper<ResourceRating> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(ResourceRating::getCreateTime);
return resourceRatingMapper.selectList(queryWrapper);
}
}

View File

@@ -0,0 +1,735 @@
package com.example.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.DoctorPatient;
import com.example.entity.SymptomRecord;
import com.example.exception.CustomException;
import com.example.mapper.SymptomRecordMapper;
import com.github.pagehelper.PageHelper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.DoubleSummaryStatistics;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
* 症状记录服务类
*/
@Service
public class SymptomRecordService {
@Resource
private SymptomRecordMapper symptomRecordMapper;
@Resource
private DoctorPatientService doctorPatientService;
/**
* 添加症状记录
*/
public void add(SymptomRecord symptomRecord) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 只有患者可以添加症状记录
if (!"patient".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.NO_AUTH_ERROR);
}
// 设置患者ID
symptomRecord.setPatientId(currentUser.getId());
// 设置记录时间和创建时间
if (symptomRecord.getRecordTime() == null) {
symptomRecord.setRecordTime(new Date());
}
symptomRecord.setCreateTime(new Date());
symptomRecordMapper.insert(symptomRecord);
}
/**
* 更新症状记录
*/
public void update(SymptomRecord symptomRecord) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 检查记录是否存在
SymptomRecord existRecord = symptomRecordMapper.selectById(symptomRecord.getId());
if (existRecord == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR);
}
// 权限检查
checkPermission(currentUser, existRecord.getPatientId());
// 更新记录
symptomRecordMapper.updateById(symptomRecord);
}
/**
* 删除症状记录
*/
public void delete(Integer id) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 检查记录是否存在
SymptomRecord existRecord = symptomRecordMapper.selectById(id);
if (existRecord == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR);
}
// 权限检查:患者只能删除自己的记录,医生不能删除记录,管理员可以删除所有记录
if ("patient".equals(currentUser.getRole())) {
if (!currentUser.getId().equals(existRecord.getPatientId())) {
throw new CustomException(ResultCodeEnum.NO_AUTH_ERROR);
}
} else if ("doctor".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.NO_AUTH_ERROR);
}
symptomRecordMapper.deleteById(id);
}
/**
* 根据ID查询症状记录
*/
public SymptomRecord getById(Integer id) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
SymptomRecord record = symptomRecordMapper.selectById(id);
if (record == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR);
}
// 权限检查
checkPermission(currentUser, record.getPatientId());
return record;
}
/**
* 根据患者ID查询所有症状记录
*/
public List<SymptomRecord> getByPatientId(Integer patientId) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 根据角色处理
if ("patient".equals(currentUser.getRole())) {
// 患者只能查看自己的记录
patientId = currentUser.getId();
} else if ("doctor".equals(currentUser.getRole())) {
// 医生只能查看自己负责的患者记录
if (patientId != null) {
// 检查该患者是否是该医生负责的
checkDoctorPatientRelationship(currentUser.getId(), patientId);
} else {
// 获取医生负责的所有患者
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), null);
List<Integer> patientIds = doctorPatients.stream()
.map(DoctorPatient::getPatientId)
.collect(Collectors.toList());
if (patientIds.isEmpty()) {
return new ArrayList<>();
}
// 查询这些患者的所有记录
LambdaQueryWrapper<SymptomRecord> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SymptomRecord::getPatientId, patientIds);
queryWrapper.orderByDesc(SymptomRecord::getRecordTime);
return symptomRecordMapper.selectList(queryWrapper);
}
}
// 查询记录
return symptomRecordMapper.selectWithPatient(patientId);
}
/**
* 分页查询症状记录
*/
public Page<SymptomRecord> getPage(Integer pageNum, Integer pageSize, String startDate, String endDate) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 创建查询条件
LambdaQueryWrapper<SymptomRecord> queryWrapper = new LambdaQueryWrapper<>();
// 根据角色设置查询条件
if ("patient".equals(currentUser.getRole())) {
// 患者只能查看自己的记录
queryWrapper.eq(SymptomRecord::getPatientId, currentUser.getId());
} else if ("doctor".equals(currentUser.getRole())) {
// 医生只能查看自己负责的患者记录
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), null);
List<Integer> patientIds = doctorPatients.stream()
.map(DoctorPatient::getPatientId)
.collect(Collectors.toList());
if (patientIds.isEmpty()) {
// 没有负责的患者,返回空结果
return new Page<>(pageNum, pageSize);
}
queryWrapper.in(SymptomRecord::getPatientId, patientIds);
}
// 添加日期条件
if (ObjectUtil.isNotEmpty(startDate) && ObjectUtil.isNotEmpty(endDate)) {
Date start = DateUtil.parseDate(startDate);
Date end = DateUtil.parseDate(endDate);
queryWrapper.between(SymptomRecord::getRecordTime, start, end);
}
// 按记录时间降序排序
queryWrapper.orderByDesc(SymptomRecord::getRecordTime);
// 执行分页查询
return symptomRecordMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
}
/**
* 检查用户是否有权限操作指定患者的记录
*/
private void checkPermission(Account currentUser, Integer patientId) {
if ("patient".equals(currentUser.getRole())) {
// 患者只能操作自己的记录
if (!currentUser.getId().equals(patientId)) {
throw new CustomException(ResultCodeEnum.NO_AUTH_ERROR);
}
} else if ("doctor".equals(currentUser.getRole())) {
// 医生只能操作自己负责的患者的记录
checkDoctorPatientRelationship(currentUser.getId(), patientId);
}
// 管理员可以操作所有记录
}
/**
* 检查医生与患者是否存在关联关系
*/
private void checkDoctorPatientRelationship(Integer doctorId, Integer patientId) {
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(doctorId, null);
boolean isResponsible = doctorPatients.stream()
.anyMatch(dp -> dp.getPatientId().equals(patientId));
if (!isResponsible) {
throw new CustomException(ResultCodeEnum.NO_AUTH_ERROR);
}
}
/**
* 批量查询症状记录关联的患者信息
*/
public List<SymptomRecord> getRecordsWithPatient(List<SymptomRecord> records) {
if (records == null || records.isEmpty()) {
return records;
}
// 从记录中提取所有患者ID
List<Integer> patientIds = records.stream()
.map(SymptomRecord::getPatientId)
.distinct()
.collect(Collectors.toList());
// 这里利用Mapper中已有的selectWithPatient方法查询所有关联患者信息的记录
List<SymptomRecord> recordsWithPatient = new ArrayList<>();
for (Integer patientId : patientIds) {
List<SymptomRecord> patientRecords = symptomRecordMapper.selectWithPatient(patientId);
recordsWithPatient.addAll(patientRecords);
}
// 根据原记录ID匹配返回带患者信息的记录
Map<Integer, SymptomRecord> recordMap = recordsWithPatient.stream()
.collect(Collectors.toMap(SymptomRecord::getId, record -> record, (a, b) -> a));
return records.stream()
.map(record -> {
SymptomRecord recordWithPatient = recordMap.get(record.getId());
return recordWithPatient != null ? recordWithPatient : record;
})
.collect(Collectors.toList());
}
/**
* 分页查询症状记录使用PageHelper
*/
public com.github.pagehelper.PageInfo<SymptomRecord> selectPage(SymptomRecord symptomRecord, Integer pageNum, Integer pageSize, String startDate, String endDate) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_INVALID_ERROR);
}
// 构建查询条件获取所有符合条件的记录
List<SymptomRecord> allRecords = new ArrayList<>();
// 获取患者名称搜索条件
final String patientName;
if (symptomRecord != null && ObjectUtil.isNotEmpty(symptomRecord.getPatientName())) {
patientName = symptomRecord.getPatientName().trim().toLowerCase();
} else {
patientName = "";
}
// 根据角色设置查询条件
if ("patient".equals(currentUser.getRole())) {
// 患者只能查看自己的记录
symptomRecord = symptomRecord == null ? new SymptomRecord() : symptomRecord;
symptomRecord.setPatientId(currentUser.getId());
allRecords = symptomRecordMapper.selectWithPatient(currentUser.getId());
} else if ("doctor".equals(currentUser.getRole())) {
// 医生只能查看自己负责的患者记录
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(currentUser.getId(), null);
List<Integer> patientIds = doctorPatients.stream()
.map(DoctorPatient::getPatientId)
.collect(Collectors.toList());
if (patientIds.isEmpty()) {
// 没有负责的患者,返回空结果
return new com.github.pagehelper.PageInfo<>(new ArrayList<>());
}
// 查询患者ID在列表中的记录并带上患者信息
for (Integer patientId : patientIds) {
List<SymptomRecord> patientRecords = symptomRecordMapper.selectWithPatient(patientId);
allRecords.addAll(patientRecords);
}
} else {
// 管理员可以查看所有记录
// 这里需要查询所有带有患者信息的记录但由于没有查询全部的接口我们先查询所有患者ID
List<SymptomRecord> tempRecords = symptomRecordMapper.selectList(null);
List<Integer> allPatientIds = tempRecords.stream()
.map(SymptomRecord::getPatientId)
.distinct()
.collect(Collectors.toList());
// 查询每个患者的记录并带上患者信息
for (Integer patientId : allPatientIds) {
List<SymptomRecord> patientRecords = symptomRecordMapper.selectWithPatient(patientId);
allRecords.addAll(patientRecords);
}
}
// 根据患者名称过滤
if (ObjectUtil.isNotEmpty(patientName)) {
allRecords = allRecords.stream()
.filter(record -> {
if (record.getPatient() != null && ObjectUtil.isNotEmpty(record.getPatient().getName())) {
return record.getPatient().getName().toLowerCase().contains(patientName);
}
return false;
})
.collect(Collectors.toList());
}
// 根据日期过滤
if (ObjectUtil.isNotEmpty(startDate) && ObjectUtil.isNotEmpty(endDate)) {
Date start = DateUtil.parseDate(startDate);
Date end = DateUtil.parseDate(endDate);
allRecords = allRecords.stream()
.filter(record -> {
Date recordTime = record.getRecordTime();
return recordTime != null && !recordTime.before(start) && !recordTime.after(end);
})
.collect(Collectors.toList());
}
// 按记录时间降序排序
allRecords.sort((a, b) -> {
if (a.getRecordTime() == null || b.getRecordTime() == null) {
return 0;
}
return b.getRecordTime().compareTo(a.getRecordTime());
});
// 在内存中执行分页
int start = (pageNum - 1) * pageSize;
int end = Math.min(start + pageSize, allRecords.size());
List<SymptomRecord> pagedRecords;
if (start < allRecords.size()) {
pagedRecords = allRecords.subList(start, end);
} else {
pagedRecords = new ArrayList<>();
}
// 创建PageInfo对象
com.github.pagehelper.PageInfo<SymptomRecord> pageInfo = new com.github.pagehelper.PageInfo<>(pagedRecords);
// 手动设置分页信息
pageInfo.setPageNum(pageNum);
pageInfo.setPageSize(pageSize);
pageInfo.setTotal(allRecords.size());
pageInfo.setPages((int) Math.ceil((double) allRecords.size() / pageSize));
return pageInfo;
}
/**
* 获取症状记录统计数据
* @param patientId 患者ID可选为null表示查询所有
* @param startDate 开始日期(可选)
* @param endDate 结束日期(可选)
* @return 统计数据
*/
public Map<String, Object> getStatisticsData(Integer patientId, Date startDate, Date endDate) {
Map<String, Object> result = new HashMap<>();
try {
// 构建查询条件
LambdaQueryWrapper<SymptomRecord> queryWrapper = new LambdaQueryWrapper<>();
if (patientId != null) {
queryWrapper.eq(SymptomRecord::getPatientId, patientId);
}
if (startDate != null) {
queryWrapper.ge(SymptomRecord::getRecordTime, startDate);
}
if (endDate != null) {
queryWrapper.le(SymptomRecord::getRecordTime, endDate);
}
// 获取所有符合条件的记录
List<SymptomRecord> records = symptomRecordMapper.selectList(queryWrapper);
// 按日期分组的严重程度平均值
Map<String, Double> severityByDate = new LinkedHashMap<>();
Map<String, Double> emotionByDate = new LinkedHashMap<>();
Map<String, Double> sleepByDate = new LinkedHashMap<>();
// 过去7天的日期
List<String> last7Days = new ArrayList<>();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
for (int i = 6; i >= 0; i--) {
calendar.setTime(new Date());
calendar.add(Calendar.DAY_OF_MONTH, -i);
String dateStr = dateFormat.format(calendar.getTime());
last7Days.add(dateStr);
// 初始化数据
severityByDate.put(dateStr, 0.0);
emotionByDate.put(dateStr, 0.0);
sleepByDate.put(dateStr, 0.0);
}
// 记录每个日期的记录数量
Map<String, Integer> countByDate = new HashMap<>();
for (String date : last7Days) {
countByDate.put(date, 0);
}
// 处理记录
for (SymptomRecord record : records) {
if (record.getRecordTime() != null) {
String recordDate = dateFormat.format(record.getRecordTime());
// 只处理过去7天的数据
if (last7Days.contains(recordDate)) {
// 更新计数
countByDate.put(recordDate, countByDate.getOrDefault(recordDate, 0) + 1);
// 更新严重程度总和
if (record.getSeverityLevel() != null) {
severityByDate.put(recordDate, severityByDate.getOrDefault(recordDate, 0.0) + record.getSeverityLevel());
}
// 更新情绪评分总和
if (record.getEmotionScore() != null) {
emotionByDate.put(recordDate, emotionByDate.getOrDefault(recordDate, 0.0) + record.getEmotionScore());
}
// 更新入睡时间总和
if (record.getSleepTime() != null) {
sleepByDate.put(recordDate, sleepByDate.getOrDefault(recordDate, 0.0) + record.getSleepTime());
}
}
}
}
// 计算每日平均值
for (String date : last7Days) {
int count = countByDate.get(date);
if (count > 0) {
severityByDate.put(date, severityByDate.get(date) / count);
emotionByDate.put(date, emotionByDate.get(date) / count);
sleepByDate.put(date, sleepByDate.get(date) / count);
}
}
// 构建返回结果
result.put("dates", last7Days);
result.put("severityData", last7Days.stream().map(severityByDate::get).collect(Collectors.toList()));
result.put("emotionData", last7Days.stream().map(emotionByDate::get).collect(Collectors.toList()));
result.put("sleepData", last7Days.stream().map(sleepByDate::get).collect(Collectors.toList()));
// 计算总体统计数据
DoubleSummaryStatistics severityStats = records.stream()
.filter(r -> r.getSeverityLevel() != null)
.mapToDouble(SymptomRecord::getSeverityLevel)
.summaryStatistics();
DoubleSummaryStatistics emotionStats = records.stream()
.filter(r -> r.getEmotionScore() != null)
.mapToDouble(SymptomRecord::getEmotionScore)
.summaryStatistics();
DoubleSummaryStatistics sleepStats = records.stream()
.filter(r -> r.getSleepTime() != null)
.mapToDouble(SymptomRecord::getSleepTime)
.summaryStatistics();
// 添加总体统计
Map<String, Object> summaryStats = new HashMap<>();
summaryStats.put("recordCount", records.size());
summaryStats.put("avgSeverity", severityStats.getAverage());
summaryStats.put("avgEmotion", emotionStats.getAverage());
summaryStats.put("avgSleepTime", sleepStats.getAverage());
result.put("summary", summaryStats);
// 计算各项分布
Map<Integer, Long> severityDistribution = records.stream()
.filter(r -> r.getSeverityLevel() != null)
.collect(Collectors.groupingBy(SymptomRecord::getSeverityLevel, Collectors.counting()));
Map<Integer, Long> emotionDistribution = records.stream()
.filter(r -> r.getEmotionScore() != null)
.collect(Collectors.groupingBy(SymptomRecord::getEmotionScore, Collectors.counting()));
result.put("severityDistribution", severityDistribution);
result.put("emotionDistribution", emotionDistribution);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 根据医生ID获取其患者的症状记录统计数据
* @param doctorId 医生ID
* @param startDate 开始日期(可选)
* @param endDate 结束日期(可选)
* @return 统计数据
*/
public Map<String, Object> getStatisticsByDoctor(Integer doctorId, Date startDate, Date endDate) {
Map<String, Object> result = new HashMap<>();
// 获取医生的所有患者
List<Integer> patientIds = new ArrayList<>();
try {
LambdaQueryWrapper<DoctorPatient> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DoctorPatient::getDoctorId, doctorId);
queryWrapper.eq(DoctorPatient::getStatus, "正常");
List<DoctorPatient> doctorPatients = doctorPatientService.selectByDoctorId(doctorId, null);
patientIds = doctorPatients.stream()
.map(DoctorPatient::getPatientId)
.collect(Collectors.toList());
} catch (Exception e) {
e.printStackTrace();
return result;
}
if (patientIds.isEmpty()) {
return result;
}
try {
// 构建查询条件
LambdaQueryWrapper<SymptomRecord> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SymptomRecord::getPatientId, patientIds);
if (startDate != null) {
queryWrapper.ge(SymptomRecord::getRecordTime, startDate);
}
if (endDate != null) {
queryWrapper.le(SymptomRecord::getRecordTime, endDate);
}
// 获取所有符合条件的记录
List<SymptomRecord> records = symptomRecordMapper.selectList(queryWrapper);
// 处理和上面的方法相同...(后续统计处理逻辑与上面的方法相同)
// 按日期分组的严重程度平均值
Map<String, Double> severityByDate = new LinkedHashMap<>();
Map<String, Double> emotionByDate = new LinkedHashMap<>();
Map<String, Double> sleepByDate = new LinkedHashMap<>();
// 过去7天的日期
List<String> last7Days = new ArrayList<>();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
for (int i = 6; i >= 0; i--) {
calendar.setTime(new Date());
calendar.add(Calendar.DAY_OF_MONTH, -i);
String dateStr = dateFormat.format(calendar.getTime());
last7Days.add(dateStr);
// 初始化数据
severityByDate.put(dateStr, 0.0);
emotionByDate.put(dateStr, 0.0);
sleepByDate.put(dateStr, 0.0);
}
// 记录每个日期的记录数量
Map<String, Integer> countByDate = new HashMap<>();
for (String date : last7Days) {
countByDate.put(date, 0);
}
// 处理记录
for (SymptomRecord record : records) {
if (record.getRecordTime() != null) {
String recordDate = dateFormat.format(record.getRecordTime());
// 只处理过去7天的数据
if (last7Days.contains(recordDate)) {
// 更新计数
countByDate.put(recordDate, countByDate.getOrDefault(recordDate, 0) + 1);
// 更新严重程度总和
if (record.getSeverityLevel() != null) {
severityByDate.put(recordDate, severityByDate.getOrDefault(recordDate, 0.0) + record.getSeverityLevel());
}
// 更新情绪评分总和
if (record.getEmotionScore() != null) {
emotionByDate.put(recordDate, emotionByDate.getOrDefault(recordDate, 0.0) + record.getEmotionScore());
}
// 更新入睡时间总和
if (record.getSleepTime() != null) {
sleepByDate.put(recordDate, sleepByDate.getOrDefault(recordDate, 0.0) + record.getSleepTime());
}
}
}
}
// 计算每日平均值
for (String date : last7Days) {
int count = countByDate.get(date);
if (count > 0) {
severityByDate.put(date, severityByDate.get(date) / count);
emotionByDate.put(date, emotionByDate.get(date) / count);
sleepByDate.put(date, sleepByDate.get(date) / count);
}
}
// 构建返回结果
result.put("dates", last7Days);
result.put("severityData", last7Days.stream().map(severityByDate::get).collect(Collectors.toList()));
result.put("emotionData", last7Days.stream().map(emotionByDate::get).collect(Collectors.toList()));
result.put("sleepData", last7Days.stream().map(sleepByDate::get).collect(Collectors.toList()));
// 计算总体统计数据
DoubleSummaryStatistics severityStats = records.stream()
.filter(r -> r.getSeverityLevel() != null)
.mapToDouble(SymptomRecord::getSeverityLevel)
.summaryStatistics();
DoubleSummaryStatistics emotionStats = records.stream()
.filter(r -> r.getEmotionScore() != null)
.mapToDouble(SymptomRecord::getEmotionScore)
.summaryStatistics();
DoubleSummaryStatistics sleepStats = records.stream()
.filter(r -> r.getSleepTime() != null)
.mapToDouble(SymptomRecord::getSleepTime)
.summaryStatistics();
// 添加总体统计
Map<String, Object> summaryStats = new HashMap<>();
summaryStats.put("recordCount", records.size());
summaryStats.put("avgSeverity", severityStats.getAverage());
summaryStats.put("avgEmotion", emotionStats.getAverage());
summaryStats.put("avgSleepTime", sleepStats.getAverage());
summaryStats.put("patientCount", patientIds.size());
result.put("summary", summaryStats);
// 计算各项分布
Map<Integer, Long> severityDistribution = records.stream()
.filter(r -> r.getSeverityLevel() != null)
.collect(Collectors.groupingBy(SymptomRecord::getSeverityLevel, Collectors.counting()));
Map<Integer, Long> emotionDistribution = records.stream()
.filter(r -> r.getEmotionScore() != null)
.collect(Collectors.groupingBy(SymptomRecord::getEmotionScore, Collectors.counting()));
result.put("severityDistribution", severityDistribution);
result.put("emotionDistribution", emotionDistribution);
// 按患者分组的数据
Map<Integer, List<SymptomRecord>> recordsByPatient = records.stream()
.collect(Collectors.groupingBy(SymptomRecord::getPatientId));
Map<Integer, Object> patientStats = new HashMap<>();
for (Map.Entry<Integer, List<SymptomRecord>> entry : recordsByPatient.entrySet()) {
Integer pid = entry.getKey();
List<SymptomRecord> patientRecords = entry.getValue();
DoubleSummaryStatistics patientSeverityStats = patientRecords.stream()
.filter(r -> r.getSeverityLevel() != null)
.mapToDouble(SymptomRecord::getSeverityLevel)
.summaryStatistics();
Map<String, Object> pStats = new HashMap<>();
pStats.put("recordCount", patientRecords.size());
pStats.put("avgSeverity", patientSeverityStats.getAverage());
pStats.put("maxSeverity", patientSeverityStats.getMax());
patientStats.put(pid, pStats);
}
result.put("patientStats", patientStats);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

View File

@@ -0,0 +1,256 @@
package com.example.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.config.TokenUtils;
import com.example.common.enums.ResultCodeEnum;
import com.example.entity.Account;
import com.example.entity.TreatmentResource;
import com.example.exception.CustomException;
import com.example.mapper.TreatmentResourceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Comparator;
import java.util.stream.Collectors;
@Service
public class TreatmentResourceService {
@Resource
private TreatmentResourceMapper treatmentResourceMapper;
public Page<TreatmentResource> search(Page<TreatmentResource> page, String keyword, String role) {
if ("admin".equals(role)) {
return treatmentResourceMapper.searchResourcesForAdmin(page, keyword);
} else {
return treatmentResourceMapper.searchResourcesForPatient(page, keyword);
}
}
public List<TreatmentResource> findByCategoryId(Integer categoryId) {
return treatmentResourceMapper.selectByCategoryId(categoryId);
}
public TreatmentResource findById(Integer id) {
return treatmentResourceMapper.selectById(id);
}
public void add(TreatmentResource resource) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 允许管理员和医生添加资源
if (!"admin".equals(currentUser.getRole()) && !"doctor".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权限添加资源");
}
// 设置默认值
resource.setPublisherId(currentUser.getId());
resource.setVisitCount(0);
resource.setDownloadCount(0);
// 如果是医生添加,则默认为待审核状态
if ("doctor".equals(currentUser.getRole())) {
resource.setAuditStatus("待审核");
} else {
// 管理员添加的资源默认已审核
resource.setAuditStatus("已通过");
}
resource.setCreateTime(LocalDateTime.now());
treatmentResourceMapper.insert(resource);
}
public void update(TreatmentResource resource) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 仅允许管理员修改资源
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权限修改资源");
}
// 检查资源是否存在
TreatmentResource dbResource = treatmentResourceMapper.selectById(resource.getId());
if (dbResource == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "资源不存在");
}
treatmentResourceMapper.updateById(resource);
}
public void delete(Integer id) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 仅允许管理员删除资源
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权限删除资源");
}
// 检查资源是否存在
TreatmentResource dbResource = treatmentResourceMapper.selectById(id);
if (dbResource == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "资源不存在");
}
treatmentResourceMapper.deleteById(id);
}
public void changeAuditStatus(Integer id, String status) {
// 获取当前登录用户
Account currentUser = TokenUtils.getCurrentUser();
if (currentUser == null) {
throw new CustomException(ResultCodeEnum.TOKEN_CHECK_ERROR);
}
// 仅允许管理员审核资源
if (!"admin".equals(currentUser.getRole())) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "无权限审核资源");
}
// 检查资源是否存在
TreatmentResource dbResource = treatmentResourceMapper.selectById(id);
if (dbResource == null) {
throw new CustomException(ResultCodeEnum.PARAM_ERROR.code, "资源不存在");
}
// 更新审核状态
TreatmentResource resource = new TreatmentResource();
resource.setId(id);
resource.setAuditStatus(status);
treatmentResourceMapper.updateById(resource);
}
public void incrementVisitCount(Integer id) {
treatmentResourceMapper.incrementVisitCount(id);
}
public void incrementDownloadCount(Integer id) {
treatmentResourceMapper.incrementDownloadCount(id);
}
public PageInfo<TreatmentResource> selectPage(TreatmentResource resource, Integer pageNum, Integer pageSize) {
// 获取当前用户角色
Account currentUser = TokenUtils.getCurrentUser();
String role = currentUser != null ? currentUser.getRole() : "patient";
// 使用PageHelper进行分页与Admin页面一致
PageHelper.startPage(pageNum, pageSize);
// 获取查询关键词
String keyword = resource.getTitle() != null ? resource.getTitle() : "";
// 直接使用Mapper的查询方法
List<TreatmentResource> list;
if ("admin".equals(role)) {
list = treatmentResourceMapper.selectAll(keyword);
} else {
list = treatmentResourceMapper.selectAllAudited(keyword);
}
// 创建PageInfo对象
return new PageInfo<>(list);
}
/**
* 查询特定发布者的资源
*/
public PageInfo<TreatmentResource> findByPublisher(TreatmentResource resource, Integer pageNum, Integer pageSize) {
// 使用PageHelper进行分页与Admin页面一致
PageHelper.startPage(pageNum, pageSize);
// 获取查询关键词
String keyword = resource.getTitle() != null ? resource.getTitle() : "";
// 调用Mapper查询方法需要添加对应的Mapper方法
List<TreatmentResource> list = treatmentResourceMapper.selectByPublisher(resource.getPublisherId(), keyword);
// 创建PageInfo对象
return new PageInfo<>(list);
}
/**
* 获取资源使用情况统计数据
* @return 资源使用情况统计数据
*/
public Map<String, Object> getResourceStatistics() {
Map<String, Object> statistics = new HashMap<>();
try {
// 查询所有资源
List<TreatmentResource> resources = treatmentResourceMapper.selectList(null);
// 按分类统计资源数量
Map<Integer, Long> countByCategory = resources.stream()
.collect(Collectors.groupingBy(TreatmentResource::getCategoryId, Collectors.counting()));
// 统计总访问次数和总下载次数
int totalVisits = resources.stream()
.mapToInt(r -> r.getVisitCount() != null ? r.getVisitCount() : 0)
.sum();
int totalDownloads = resources.stream()
.mapToInt(r -> r.getDownloadCount() != null ? r.getDownloadCount() : 0)
.sum();
// 访问次数最多的资源TOP5
List<TreatmentResource> topVisitResources = resources.stream()
.sorted(Comparator.comparing(r -> r.getVisitCount() != null ? -r.getVisitCount() : 0))
.limit(5)
.collect(Collectors.toList());
// 下载次数最多的资源TOP5
List<TreatmentResource> topDownloadResources = resources.stream()
.sorted(Comparator.comparing(r -> r.getDownloadCount() != null ? -r.getDownloadCount() : 0))
.limit(5)
.collect(Collectors.toList());
// 按资源分类统计访问次数和下载次数
Map<Integer, Integer> visitsByCategory = new HashMap<>();
Map<Integer, Integer> downloadsByCategory = new HashMap<>();
for (TreatmentResource resource : resources) {
Integer categoryId = resource.getCategoryId();
Integer visitCount = resource.getVisitCount() != null ? resource.getVisitCount() : 0;
Integer downloadCount = resource.getDownloadCount() != null ? resource.getDownloadCount() : 0;
visitsByCategory.put(categoryId, visitsByCategory.getOrDefault(categoryId, 0) + visitCount);
downloadsByCategory.put(categoryId, downloadsByCategory.getOrDefault(categoryId, 0) + downloadCount);
}
// 构建返回结果
statistics.put("totalResources", resources.size());
statistics.put("totalVisits", totalVisits);
statistics.put("totalDownloads", totalDownloads);
statistics.put("countByCategory", countByCategory);
statistics.put("visitsByCategory", visitsByCategory);
statistics.put("downloadsByCategory", downloadsByCategory);
statistics.put("topVisitResources", topVisitResources);
statistics.put("topDownloadResources", topDownloadResources);
} catch (Exception e) {
e.printStackTrace();
}
return statistics;
}
}

View File

@@ -0,0 +1,195 @@
package com.example.websocket;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.example.entity.ChatMessage;
import com.example.service.ChatMessageService;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/ws/chat/{userId}/{userType}")
@Component
public class ChatWebSocket {
private static final Logger log = LoggerFactory.getLogger(ChatWebSocket.class);
// 静态变量,用来记录当前在线连接数
private static int onlineCount = 0;
// 用户ID和WebSocket的映射关系
private static Map<String, ChatWebSocket> clients = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,用于发送数据
private Session session;
// 当前连接用户ID
private Integer userId;
// 当前连接用户类型
private String userType;
// 注入Service因为@ServerEndpoint不支持直接注入需要通过静态变量
private static ChatMessageService chatMessageService;
private static final ObjectMapper objectMapper = new ObjectMapper();
@Resource
public void setChatMessageService(ChatMessageService chatMessageService) {
ChatWebSocket.chatMessageService = chatMessageService;
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") Integer userId, @PathParam("userType") String userType) {
this.session = session;
this.userId = userId;
this.userType = userType;
// 将当前WebSocket对象加入到Map中
String key = userId + ":" + userType;
clients.put(key, this);
addOnlineCount();
log.info("有新连接加入,当前在线人数为:{}", getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 从Map中移除
String key = userId + ":" + userType;
clients.remove(key);
subOnlineCount();
log.info("有一连接关闭,当前在线人数为:{}", getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自用户{}:{}的消息:{}", userId, userType, message);
try {
// 解析消息
ChatMessage chatMessage = objectMapper.readValue(message, ChatMessage.class);
// 直接设置发送者信息不再通过TokenUtils获取
chatMessage.setSenderId(userId);
chatMessage.setSenderType(userType);
chatMessage.setSendTime(new Date());
chatMessage.setIsRead(false);
// 保存临时ID用于前端识别消息
String tempId = null;
try {
// 从消息中提取tempId字段
Map<String, Object> messageMap = objectMapper.readValue(message, Map.class);
if (messageMap.containsKey("tempId")) {
tempId = messageMap.get("tempId").toString();
}
} catch (Exception e) {
log.warn("提取tempId失败", e);
}
// 保存消息到数据库
try {
// 检查发送者和接收者是否有关联关系并保存消息
chatMessageService.directSaveMessage(chatMessage);
// 将保存后的消息转为Map以便添加tempId
Map<String, Object> responseMap = objectMapper.convertValue(chatMessage, Map.class);
if (tempId != null) {
responseMap.put("tempId", tempId);
}
String responseJson = objectMapper.writeValueAsString(responseMap);
// 转发消息给接收者
String receiverKey = chatMessage.getReceiverId() + ":" + chatMessage.getReceiverType();
ChatWebSocket receiverSocket = clients.get(receiverKey);
if (receiverSocket != null) {
// 接收者在线,发送消息
receiverSocket.sendMessage(responseJson);
}
// 同时也返回给发送者
sendMessage(responseJson);
} catch (Exception e) {
log.error("处理消息时发生错误", e);
// 发送错误消息给客户端
ChatMessage errorMessage = new ChatMessage();
errorMessage.setSenderId(0);
errorMessage.setSenderType("system");
errorMessage.setReceiverId(userId);
errorMessage.setReceiverType(userType);
errorMessage.setContent("发送消息失败:" + e.getMessage());
errorMessage.setSendTime(new Date());
// 将错误消息转为Map以便添加tempId
Map<String, Object> errorMap = objectMapper.convertValue(errorMessage, Map.class);
if (tempId != null) {
errorMap.put("tempId", tempId);
}
sendMessage(objectMapper.writeValueAsString(errorMap));
}
} catch (Exception e) {
log.error("处理消息时发生错误", e);
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误", error);
}
/**
* 发送消息
*/
public void sendMessage(String message) {
try {
this.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("发送消息失败", e);
}
}
/**
* 服务端主动推送消息
*/
public static void sendMessage(Integer userId, String userType, String message) {
String key = userId + ":" + userType;
ChatWebSocket socket = clients.get(key);
if (socket != null) {
socket.sendMessage(message);
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
ChatWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
ChatWebSocket.onlineCount--;
}
}

View File

@@ -0,0 +1,28 @@
server:
port: 9090
# 数据库配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/psychologicaltreatment?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8&allowPublicKeyRetrieval=true
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
# 配置mybatis实体和xml映射
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
fileBaseUrl: http://localhost:${server.port}
# 通义千问API配置
dashscope:
api-key: sk-0b1b6c3287364c2d8b8e1f656bfafece
model: qwen-turbo

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.AdminMapper">
<select id="selectAll" resultType="com.example.entity.Admin">
select `admin`.* from `admin`
<where>
<if test="username != null and username != ''">
and `admin`.`username` like concat ('%', #{username}, '%')
</if>
</where>
</select>
<select id="selectById" resultType="com.example.entity.Admin">
select * from admin where id = #{id}
</select>
<delete id="deleteById">
delete from admin where id = #{id}
</delete>
</mapper>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.DoctorMapper">
<select id="selectAll" resultType="com.example.entity.Doctor">
select `doctor`.* from `doctor`
<where>
<if test="name != null and name != ''">
and `doctor`.`name` like concat ('%', #{name}, '%')
</if>
</where>
</select>
<select id="selectById" resultType="com.example.entity.Doctor">
select * from doctor where id = #{id}
</select>
<delete id="deleteById">
delete from doctor where id = #{id}
</delete>
</mapper>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.DoctorPatientMapper">
<resultMap id="doctorPatientMap" type="com.example.entity.DoctorPatient">
<id property="id" column="id"/>
<result property="doctorId" column="doctor_id"/>
<result property="patientId" column="patient_id"/>
<result property="relationshipStart" column="relationship_start"/>
<result property="status" column="status"/>
<result property="createTime" column="create_time"/>
<association property="doctor" javaType="com.example.entity.Doctor">
<id property="id" column="d_id"/>
<result property="username" column="d_username"/>
<result property="name" column="d_name"/>
<result property="sex" column="d_sex"/>
<result property="phone" column="d_phone"/>
<result property="email" column="d_email"/>
<result property="certificateNo" column="d_certificate_no"/>
<result property="hospital" column="d_hospital"/>
<result property="department" column="d_department"/>
</association>
<association property="patient" javaType="com.example.entity.Patient">
<id property="id" column="p_id"/>
<result property="username" column="p_username"/>
<result property="name" column="p_name"/>
<result property="sex" column="p_sex"/>
<result property="phone" column="p_phone"/>
<result property="email" column="p_email"/>
</association>
</resultMap>
<select id="selectByDoctorId" resultMap="doctorPatientMap">
SELECT
dp.*,
p.id AS p_id,
p.username AS p_username,
p.name AS p_name,
p.sex AS p_sex,
p.phone AS p_phone,
p.email AS p_email,
d.id AS d_id,
d.username AS d_username,
d.name AS d_name
FROM
doctor_patient dp
LEFT JOIN patient p ON dp.patient_id = p.id
LEFT JOIN doctor d ON dp.doctor_id = d.id
WHERE
dp.doctor_id = #{doctorId}
<if test="name != null and name != ''">
AND p.name LIKE CONCAT('%', #{name}, '%')
</if>
ORDER BY
dp.create_time DESC
</select>
<select id="selectByPatientId" resultMap="doctorPatientMap">
SELECT
dp.*,
p.id AS p_id,
p.username AS p_username,
p.name AS p_name,
p.sex AS p_sex,
p.phone AS p_phone,
p.email AS p_email,
d.id AS d_id,
d.username AS d_username,
d.name AS d_name,
d.sex AS d_sex,
d.phone AS d_phone,
d.email AS d_email,
d.certificate_no AS d_certificate_no,
d.hospital AS d_hospital,
d.department AS d_department
FROM
doctor_patient dp
LEFT JOIN patient p ON dp.patient_id = p.id
LEFT JOIN doctor d ON dp.doctor_id = d.id
WHERE
dp.patient_id = #{patientId}
<if test="name != null and name != ''">
AND d.name LIKE CONCAT('%', #{name}, '%')
</if>
ORDER BY
dp.create_time DESC
</select>
</mapper>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.PatientMapper">
<select id="selectAll" resultType="com.example.entity.Patient">
select `patient`.* from `patient`
<where>
<if test="name != null and name != ''">
and `patient`.`name` like concat ('%', #{name}, '%')
</if>
</where>
</select>
<select id="selectById" resultType="com.example.entity.Patient">
select * from patient where id = #{id}
</select>
<delete id="deleteById">
delete from patient where id = #{id}
</delete>
</mapper>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.SymptomRecordMapper">
<resultMap id="symptomRecordMap" type="com.example.entity.SymptomRecord">
<id property="id" column="id"/>
<result property="patientId" column="patient_id"/>
<result property="symptomDesc" column="symptom_desc"/>
<result property="severityLevel" column="severity_level"/>
<result property="emotionScore" column="emotion_score"/>
<result property="energyDuration" column="energy_duration"/>
<result property="interestRecovery" column="interest_recovery"/>
<result property="sleepTime" column="sleep_time"/>
<result property="wakeCount" column="wake_count"/>
<result property="morningWake" column="morning_wake"/>
<result property="mealCompletion" column="meal_completion"/>
<result property="painLocation" column="pain_location"/>
<result property="painIntensity" column="pain_intensity"/>
<result property="memoryTest" column="memory_test"/>
<result property="decisionTime" column="decision_time"/>
<result property="attentionSpan" column="attention_span"/>
<result property="suicidalThought" column="suicidal_thought"/>
<result property="recordTime" column="record_time"/>
<result property="createTime" column="create_time"/>
<!-- 关联患者信息 -->
<association property="patient" javaType="com.example.entity.Patient">
<id property="id" column="p_id"/>
<result property="username" column="p_username"/>
<result property="name" column="p_name"/>
<result property="avatar" column="p_avatar"/>
<result property="sex" column="p_sex"/>
<result property="phone" column="p_phone"/>
<result property="email" column="p_email"/>
</association>
</resultMap>
<!-- 查询记录并带患者信息 -->
<select id="selectWithPatient" resultMap="symptomRecordMap">
SELECT
sr.*,
p.id AS p_id,
p.username AS p_username,
p.name AS p_name,
p.avatar AS p_avatar,
p.sex AS p_sex,
p.phone AS p_phone,
p.email AS p_email
FROM
symptom_record sr
LEFT JOIN
patient p ON sr.patient_id = p.id
<where>
<if test="patientId != null">
AND sr.patient_id = #{patientId}
</if>
</where>
ORDER BY
sr.record_time DESC
</select>
</mapper>