diff --git a/README.md b/README.md index e69de29..211faca 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,37 @@ +# Nursing Home Management + +This repository contains a Spring Boot backend and a Vue 2 + ElementUI frontend for a nursing home management system. + +## Tech Stack +- Backend: Spring Boot 3, Sa-Token, MyBatis, MySQL +- Frontend: Vue 2, Axios, ElementUI + +## Setup + +### 1) Database +1. Create a MySQL database and import the schema: + - File: backend/sql/schema.sql +2. Update database connection in backend/src/main/resources/application.yml. + +Default admin account: +- username: admin +- password: admin123 + +### 2) Backend +```bash +cd backend +mvn spring-boot:run +``` + +### 3) Frontend +```bash +cd frontend +npm install +npm run serve +``` + +Frontend will run at http://localhost:5173 and proxy to the backend at http://localhost:8080. + +## Notes +- Family users can register via the Register page (needs matching elder ID card in the database). +- File uploads are stored in the backend `uploads` folder and served at `/files/*`. diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 0000000..b9a410f --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + + com.nursinghome + nursing-home-backend + 1.0.0 + nursing-home-backend + + + 17 + 3.2.5 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 3.0.3 + + + com.mysql + mysql-connector-j + + + cn.dev33 + sa-token-spring-boot3-starter + 1.37.0 + + + org.springframework.security + spring-security-crypto + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/backend/sql/schema.sql b/backend/sql/schema.sql new file mode 100644 index 0000000..4733fd8 --- /dev/null +++ b/backend/sql/schema.sql @@ -0,0 +1,143 @@ +CREATE DATABASE IF NOT EXISTS nursing_home DEFAULT CHARACTER SET utf8mb4; +USE nursing_home; + +DROP TABLE IF EXISTS payment_record; +DROP TABLE IF EXISTS bill; +DROP TABLE IF EXISTS feedback; +DROP TABLE IF EXISTS notice; +DROP TABLE IF EXISTS handover; +DROP TABLE IF EXISTS health_record; +DROP TABLE IF EXISTS care_record; +DROP TABLE IF EXISTS schedule; +DROP TABLE IF EXISTS family_elder; +DROP TABLE IF EXISTS elder; +DROP TABLE IF EXISTS sys_user; + +CREATE TABLE sys_user ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(100) NOT NULL, + name VARCHAR(50) NOT NULL, + phone VARCHAR(30), + role VARCHAR(20) NOT NULL, + status TINYINT NOT NULL DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE elder ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(50) NOT NULL, + gender VARCHAR(10), + id_card VARCHAR(30) NOT NULL UNIQUE, + birthday DATE, + room_no VARCHAR(20), + check_in_date DATE, + care_level VARCHAR(20), + status VARCHAR(20), + remark VARCHAR(200) +); + +CREATE TABLE family_elder ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + family_id BIGINT NOT NULL, + elder_id BIGINT NOT NULL, + relationship VARCHAR(20), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_family (family_id), + INDEX idx_elder (elder_id) +); + +CREATE TABLE schedule ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + nurse_id BIGINT NOT NULL, + date DATE NOT NULL, + shift VARCHAR(20), + task VARCHAR(200), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_nurse_date (nurse_id, date) +); + +CREATE TABLE care_record ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + elder_id BIGINT NOT NULL, + nurse_id BIGINT NOT NULL, + content VARCHAR(500), + attachment_url VARCHAR(200), + record_time DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_elder (elder_id) +); + +CREATE TABLE health_record ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + elder_id BIGINT NOT NULL, + nurse_id BIGINT NOT NULL, + temperature DECIMAL(4,1), + bp_systolic INT, + bp_diastolic INT, + heart_rate INT, + note VARCHAR(200), + record_time DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_elder (elder_id) +); + +CREATE TABLE handover ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + nurse_id BIGINT NOT NULL, + date DATE NOT NULL, + content VARCHAR(500), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_nurse (nurse_id) +); + +CREATE TABLE notice ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(100) NOT NULL, + content VARCHAR(1000), + target_role VARCHAR(20), + target_user_id BIGINT, + created_by BIGINT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE feedback ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + family_id BIGINT NOT NULL, + elder_id BIGINT NOT NULL, + type VARCHAR(20), + content VARCHAR(500), + rating INT, + status VARCHAR(20), + reply VARCHAR(500), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE bill ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + elder_id BIGINT NOT NULL, + month VARCHAR(7) NOT NULL, + bed_fee DECIMAL(10,2), + care_fee DECIMAL(10,2), + meal_fee DECIMAL(10,2), + other_fee DECIMAL(10,2), + total DECIMAL(10,2), + status VARCHAR(20), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + paid_at DATETIME +); + +CREATE TABLE payment_record ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + bill_id BIGINT NOT NULL, + family_id BIGINT NOT NULL, + amount DECIMAL(10,2) NOT NULL, + method VARCHAR(20), + paid_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +INSERT INTO sys_user(username, password, name, phone, role, status) +VALUES ('admin', 'admin123', 'Administrator', '13800000000', 'ADMIN', 1); diff --git a/backend/src/main/java/com/nursinghome/NursingHomeApplication.java b/backend/src/main/java/com/nursinghome/NursingHomeApplication.java new file mode 100644 index 0000000..b1445ed --- /dev/null +++ b/backend/src/main/java/com/nursinghome/NursingHomeApplication.java @@ -0,0 +1,13 @@ +package com.nursinghome; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.nursinghome.mapper") +public class NursingHomeApplication { + public static void main(String[] args) { + SpringApplication.run(NursingHomeApplication.class, args); + } +} diff --git a/backend/src/main/java/com/nursinghome/common/ApiException.java b/backend/src/main/java/com/nursinghome/common/ApiException.java new file mode 100644 index 0000000..f3dfa4d --- /dev/null +++ b/backend/src/main/java/com/nursinghome/common/ApiException.java @@ -0,0 +1,7 @@ +package com.nursinghome.common; + +public class ApiException extends RuntimeException { + public ApiException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/nursinghome/common/ApiResponse.java b/backend/src/main/java/com/nursinghome/common/ApiResponse.java new file mode 100644 index 0000000..d079e36 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/common/ApiResponse.java @@ -0,0 +1,51 @@ +package com.nursinghome.common; + +public class ApiResponse { + private int code; + private String message; + private T data; + + public ApiResponse() {} + + public ApiResponse(int code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static ApiResponse success(T data) { + return new ApiResponse<>(0, "ok", data); + } + + public static ApiResponse success() { + return new ApiResponse<>(0, "ok", null); + } + + public static ApiResponse error(String message) { + return new ApiResponse<>(1, message, null); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/backend/src/main/java/com/nursinghome/common/GlobalExceptionHandler.java b/backend/src/main/java/com/nursinghome/common/GlobalExceptionHandler.java new file mode 100644 index 0000000..1f4b041 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/common/GlobalExceptionHandler.java @@ -0,0 +1,25 @@ +package com.nursinghome.common; + +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(ApiException.class) + public ApiResponse handleApi(ApiException ex) { + return ApiResponse.error(ex.getMessage()); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ApiResponse handleValid(MethodArgumentNotValidException ex) { + String msg = ex.getBindingResult().getAllErrors().isEmpty() + ? "validation error" : ex.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + return ApiResponse.error(msg); + } + + @ExceptionHandler(Exception.class) + public ApiResponse handle(Exception ex) { + return ApiResponse.error(ex.getMessage()); + } +} diff --git a/backend/src/main/java/com/nursinghome/config/CorsConfig.java b/backend/src/main/java/com/nursinghome/config/CorsConfig.java new file mode 100644 index 0000000..5942b33 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/config/CorsConfig.java @@ -0,0 +1,17 @@ +package com.nursinghome.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } +} diff --git a/backend/src/main/java/com/nursinghome/config/SaTokenStpInterfaceImpl.java b/backend/src/main/java/com/nursinghome/config/SaTokenStpInterfaceImpl.java new file mode 100644 index 0000000..e5446e6 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/config/SaTokenStpInterfaceImpl.java @@ -0,0 +1,32 @@ +package com.nursinghome.config; + +import cn.dev33.satoken.stp.StpInterface; +import com.nursinghome.entity.User; +import com.nursinghome.mapper.UserMapper; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +@Component +public class SaTokenStpInterfaceImpl implements StpInterface { + private final UserMapper userMapper; + + public SaTokenStpInterfaceImpl(UserMapper userMapper) { + this.userMapper = userMapper; + } + + @Override + public List getPermissionList(Object loginId, String loginType) { + return Collections.emptyList(); + } + + @Override + public List getRoleList(Object loginId, String loginType) { + User user = userMapper.findById(Long.valueOf(loginId.toString())); + if (user == null) { + return Collections.emptyList(); + } + return Collections.singletonList(user.getRole()); + } +} diff --git a/backend/src/main/java/com/nursinghome/config/WebConfig.java b/backend/src/main/java/com/nursinghome/config/WebConfig.java new file mode 100644 index 0000000..ba7318d --- /dev/null +++ b/backend/src/main/java/com/nursinghome/config/WebConfig.java @@ -0,0 +1,23 @@ +package com.nursinghome.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.nio.file.Path; +import java.nio.file.Paths; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Value("${app.upload-dir}") + private String uploadDir; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + Path uploadPath = Paths.get(uploadDir).toAbsolutePath().normalize(); + String location = uploadPath.toUri().toString(); + registry.addResourceHandler("/files/**") + .addResourceLocations(location); + } +} diff --git a/backend/src/main/java/com/nursinghome/controller/AdminController.java b/backend/src/main/java/com/nursinghome/controller/AdminController.java new file mode 100644 index 0000000..53c090a --- /dev/null +++ b/backend/src/main/java/com/nursinghome/controller/AdminController.java @@ -0,0 +1,246 @@ +package com.nursinghome.controller; + +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.stp.StpUtil; +import com.nursinghome.common.ApiException; +import com.nursinghome.common.ApiResponse; +import com.nursinghome.dto.*; +import com.nursinghome.entity.*; +import com.nursinghome.mapper.*; +import com.nursinghome.service.BillService; +import com.nursinghome.service.ElderService; +import com.nursinghome.service.UserService; +import com.nursinghome.util.PasswordUtil; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/admin") +@SaCheckRole("ADMIN") +public class AdminController { + private final UserService userService; + private final ElderService elderService; + private final ScheduleMapper scheduleMapper; + private final FeedbackMapper feedbackMapper; + private final NoticeMapper noticeMapper; + private final BillService billService; + private final BillMapper billMapper; + private final UserMapper userMapper; + + public AdminController(UserService userService, + ElderService elderService, + ScheduleMapper scheduleMapper, + FeedbackMapper feedbackMapper, + NoticeMapper noticeMapper, + BillService billService, + BillMapper billMapper, + UserMapper userMapper) { + this.userService = userService; + this.elderService = elderService; + this.scheduleMapper = scheduleMapper; + this.feedbackMapper = feedbackMapper; + this.noticeMapper = noticeMapper; + this.billService = billService; + this.billMapper = billMapper; + this.userMapper = userMapper; + } + + @GetMapping("/stats") + public ApiResponse> stats() { + int elders = elderService.listAll().size(); + int nurses = userService.listByRole("NURSE").size(); + int families = userService.listByRole("FAMILY").size(); + BigDecimal income = BigDecimal.ZERO; + for (Bill bill : billMapper.listAll()) { + if ("PAID".equals(bill.getStatus()) && bill.getTotal() != null) { + income = income.add(bill.getTotal()); + } + } + Map data = new HashMap<>(); + data.put("elders", elders); + data.put("nurses", nurses); + data.put("families", families); + data.put("income", income); + return ApiResponse.success(data); + } + + @GetMapping("/users") + public ApiResponse> listUsers(@RequestParam(required = false) String role) { + List users = userService.listByRole(role); + for (User user : users) { + user.setPassword(null); + } + return ApiResponse.success(users); + } + + @PostMapping("/users") + public ApiResponse createUser(@RequestBody UserRequest request) { + User user = new User(); + user.setUsername(request.getUsername()); + user.setPassword(request.getPassword()); + user.setName(request.getName()); + user.setPhone(request.getPhone()); + user.setRole(request.getRole()); + user.setStatus(request.getStatus() == null ? 1 : request.getStatus()); + userService.createUser(user, true); + user.setPassword(null); + return ApiResponse.success(user); + } + + @PutMapping("/users") + public ApiResponse updateUser(@RequestBody UserUpdateRequest request) { + User user = new User(); + user.setId(request.getId()); + user.setName(request.getName()); + user.setPhone(request.getPhone()); + user.setStatus(request.getStatus()); + userService.updateUser(user); + return ApiResponse.success(); + } + + @PutMapping("/users/{id}/status") + public ApiResponse updateUserStatus(@PathVariable Long id, @RequestParam Integer status) { + userService.updateStatus(id, status); + return ApiResponse.success(); + } + + @PostMapping("/users/{id}/reset-password") + public ApiResponse resetPassword(@PathVariable Long id, @RequestParam String password) { + userService.updatePassword(id, PasswordUtil.hash(password)); + return ApiResponse.success(); + } + + @GetMapping("/elders") + public ApiResponse> listElders() { + return ApiResponse.success(elderService.listAll()); + } + + @PostMapping("/elders") + public ApiResponse createElder(@RequestBody ElderRequest request) { + Elder elder = new Elder(); + elder.setName(request.getName()); + elder.setGender(request.getGender()); + elder.setIdCard(request.getIdCard()); + elder.setBirthday(request.getBirthday()); + elder.setRoomNo(request.getRoomNo()); + elder.setCheckInDate(request.getCheckInDate()); + elder.setCareLevel(request.getCareLevel()); + elder.setStatus(request.getStatus()); + elder.setRemark(request.getRemark()); + return ApiResponse.success(elderService.create(elder)); + } + + @PutMapping("/elders") + public ApiResponse updateElder(@RequestBody ElderRequest request) { + Elder elder = new Elder(); + elder.setId(request.getId()); + elder.setName(request.getName()); + elder.setGender(request.getGender()); + elder.setIdCard(request.getIdCard()); + elder.setBirthday(request.getBirthday()); + elder.setRoomNo(request.getRoomNo()); + elder.setCheckInDate(request.getCheckInDate()); + elder.setCareLevel(request.getCareLevel()); + elder.setStatus(request.getStatus()); + elder.setRemark(request.getRemark()); + elderService.update(elder); + return ApiResponse.success(); + } + + @DeleteMapping("/elders/{id}") + public ApiResponse deleteElder(@PathVariable Long id) { + elderService.delete(id); + return ApiResponse.success(); + } + + @GetMapping("/schedules") + public ApiResponse> listSchedules(@RequestParam String date) { + return ApiResponse.success(scheduleMapper.listByDate(java.time.LocalDate.parse(date))); + } + + @PostMapping("/schedules") + public ApiResponse createSchedule(@RequestBody ScheduleRequest request) { + Schedule schedule = new Schedule(); + schedule.setNurseId(request.getNurseId()); + schedule.setDate(request.getDate()); + schedule.setShift(request.getShift()); + schedule.setTask(request.getTask()); + scheduleMapper.insert(schedule); + return ApiResponse.success(schedule); + } + + @PutMapping("/schedules") + public ApiResponse updateSchedule(@RequestBody ScheduleRequest request) { + Schedule schedule = new Schedule(); + schedule.setId(request.getId()); + schedule.setNurseId(request.getNurseId()); + schedule.setDate(request.getDate()); + schedule.setShift(request.getShift()); + schedule.setTask(request.getTask()); + scheduleMapper.update(schedule); + return ApiResponse.success(); + } + + @DeleteMapping("/schedules/{id}") + public ApiResponse deleteSchedule(@PathVariable Long id) { + scheduleMapper.delete(id); + return ApiResponse.success(); + } + + @PostMapping("/bills") + public ApiResponse createBill(@RequestBody BillRequest request) { + Bill bill = new Bill(); + bill.setElderId(request.getElderId()); + bill.setMonth(request.getMonth()); + bill.setBedFee(request.getBedFee()); + bill.setCareFee(request.getCareFee()); + bill.setMealFee(request.getMealFee()); + bill.setOtherFee(request.getOtherFee()); + return ApiResponse.success(billService.createBill(bill)); + } + + @GetMapping("/bills") + public ApiResponse> listBills(@RequestParam(required = false) Long elderId) { + if (elderId == null) { + return ApiResponse.success(billMapper.listAll()); + } + return ApiResponse.success(billService.listByElderId(elderId)); + } + + @GetMapping("/feedback") + public ApiResponse> listFeedback() { + return ApiResponse.success(feedbackMapper.listAll()); + } + + @PutMapping("/feedback") + public ApiResponse updateFeedback(@RequestBody FeedbackUpdateRequest request) { + Feedback feedback = new Feedback(); + feedback.setId(request.getId()); + feedback.setStatus(request.getStatus()); + feedback.setReply(request.getReply()); + feedbackMapper.updateStatus(feedback); + return ApiResponse.success(); + } + + @PostMapping("/notices") + public ApiResponse createNotice(@RequestBody NoticeRequest request) { + Notice notice = new Notice(); + notice.setTitle(request.getTitle()); + notice.setContent(request.getContent()); + notice.setTargetRole(request.getTargetRole()); + notice.setTargetUserId(request.getTargetUserId()); + notice.setCreatedBy(Long.valueOf(StpUtil.getLoginId().toString())); + noticeMapper.insert(notice); + return ApiResponse.success(notice); + } + + @GetMapping("/notices") + public ApiResponse> listNotices(@RequestParam(required = false) String role, + @RequestParam(required = false) Long userId) { + return ApiResponse.success(noticeMapper.listByTarget(role, userId)); + } +} diff --git a/backend/src/main/java/com/nursinghome/controller/AuthController.java b/backend/src/main/java/com/nursinghome/controller/AuthController.java new file mode 100644 index 0000000..06b307a --- /dev/null +++ b/backend/src/main/java/com/nursinghome/controller/AuthController.java @@ -0,0 +1,84 @@ +package com.nursinghome.controller; + +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.stp.StpUtil; +import com.nursinghome.common.ApiException; +import com.nursinghome.common.ApiResponse; +import com.nursinghome.dto.LoginRequest; +import com.nursinghome.dto.RegisterRequest; +import com.nursinghome.entity.User; +import com.nursinghome.service.FamilyService; +import com.nursinghome.service.UserService; +import com.nursinghome.util.PasswordUtil; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/auth") +public class AuthController { + private final UserService userService; + private final FamilyService familyService; + + public AuthController(UserService userService, FamilyService familyService) { + this.userService = userService; + this.familyService = familyService; + } + + @PostMapping("/login") + public ApiResponse> login(@Valid @RequestBody LoginRequest request) { + User user = userService.findByUsername(request.getUsername()); + if (user == null) { + throw new ApiException("user not found"); + } + if (user.getStatus() != null && user.getStatus() == 0) { + throw new ApiException("user disabled"); + } + if (!PasswordUtil.matches(request.getPassword(), user.getPassword())) { + throw new ApiException("invalid password"); + } + if (!PasswordUtil.isBcrypt(user.getPassword())) { + userService.updatePassword(user.getId(), PasswordUtil.hash(request.getPassword())); + } + StpUtil.login(user.getId()); + + user.setPassword(null); + Map data = new HashMap<>(); + data.put("token", StpUtil.getTokenValue()); + data.put("role", user.getRole()); + data.put("user", user); + return ApiResponse.success(data); + } + + @PostMapping("/register") + public ApiResponse register(@Valid @RequestBody RegisterRequest request) { + User user = familyService.registerFamily( + request.getUsername(), + request.getPassword(), + request.getName(), + request.getPhone(), + request.getElderIdCard(), + request.getRelationship() + ); + return ApiResponse.success(user); + } + + @PostMapping("/logout") + public ApiResponse logout() { + StpUtil.logout(); + return ApiResponse.success(); + } + + @GetMapping("/me") + @SaCheckLogin + public ApiResponse me() { + Long userId = Long.valueOf(StpUtil.getLoginId().toString()); + User user = userService.findById(userId); + if (user != null) { + user.setPassword(null); + } + return ApiResponse.success(user); + } +} diff --git a/backend/src/main/java/com/nursinghome/controller/FamilyController.java b/backend/src/main/java/com/nursinghome/controller/FamilyController.java new file mode 100644 index 0000000..8bcc725 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/controller/FamilyController.java @@ -0,0 +1,157 @@ +package com.nursinghome.controller; + +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.stp.StpUtil; +import com.nursinghome.common.ApiException; +import com.nursinghome.common.ApiResponse; +import com.nursinghome.dto.FeedbackRequest; +import com.nursinghome.dto.PayRequest; +import com.nursinghome.entity.*; +import com.nursinghome.mapper.*; +import com.nursinghome.service.BillService; +import com.nursinghome.service.FamilyService; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/api/family") +@SaCheckRole("FAMILY") +public class FamilyController { + private final FamilyService familyService; + private final ElderMapper elderMapper; + private final CareRecordMapper careRecordMapper; + private final HealthRecordMapper healthRecordMapper; + private final BillService billService; + private final BillMapper billMapper; + private final NoticeMapper noticeMapper; + private final FeedbackMapper feedbackMapper; + + public FamilyController(FamilyService familyService, + ElderMapper elderMapper, + CareRecordMapper careRecordMapper, + HealthRecordMapper healthRecordMapper, + BillService billService, + BillMapper billMapper, + NoticeMapper noticeMapper, + FeedbackMapper feedbackMapper) { + this.familyService = familyService; + this.elderMapper = elderMapper; + this.careRecordMapper = careRecordMapper; + this.healthRecordMapper = healthRecordMapper; + this.billService = billService; + this.billMapper = billMapper; + this.noticeMapper = noticeMapper; + this.feedbackMapper = feedbackMapper; + } + + @GetMapping("/elders") + public ApiResponse> listElders() { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + List relations = familyService.listRelations(familyId); + List elders = new ArrayList<>(); + for (FamilyElder relation : relations) { + Elder elder = elderMapper.findById(relation.getElderId()); + if (elder != null) { + elders.add(elder); + } + } + return ApiResponse.success(elders); + } + + @GetMapping("/care-records") + public ApiResponse> listCare(@RequestParam Long elderId) { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + boolean allowed = false; + for (FamilyElder relation : familyService.listRelations(familyId)) { + if (relation.getElderId().equals(elderId)) { + allowed = true; + break; + } + } + if (!allowed) { + throw new ApiException("no access to this elder"); + } + return ApiResponse.success(careRecordMapper.listByElderId(elderId)); + } + + @GetMapping("/health-records") + public ApiResponse> listHealth(@RequestParam Long elderId) { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + boolean allowed = false; + for (FamilyElder relation : familyService.listRelations(familyId)) { + if (relation.getElderId().equals(elderId)) { + allowed = true; + break; + } + } + if (!allowed) { + throw new ApiException("no access to this elder"); + } + return ApiResponse.success(healthRecordMapper.listByElderId(elderId)); + } + + @GetMapping("/bills") + public ApiResponse> listBills(@RequestParam Long elderId) { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + boolean allowed = false; + for (FamilyElder relation : familyService.listRelations(familyId)) { + if (relation.getElderId().equals(elderId)) { + allowed = true; + break; + } + } + if (!allowed) { + throw new ApiException("no access to this elder"); + } + return ApiResponse.success(billService.listByElderId(elderId)); + } + + @PostMapping("/bills/{id}/pay") + public ApiResponse pay(@PathVariable Long id, @RequestBody PayRequest request) { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + Bill bill = billMapper.findById(id); + if (bill == null) { + throw new ApiException("bill not found"); + } + boolean allowed = false; + for (FamilyElder relation : familyService.listRelations(familyId)) { + if (relation.getElderId().equals(bill.getElderId())) { + allowed = true; + break; + } + } + if (!allowed) { + throw new ApiException("no access to this bill"); + } + if ("PAID".equals(bill.getStatus())) { + throw new ApiException("bill already paid"); + } + BigDecimal amount = bill.getTotal() == null ? BigDecimal.ZERO : bill.getTotal(); + billService.payBill(id, familyId, request.getMethod(), amount); + return ApiResponse.success(); + } + + @PostMapping("/feedback") + public ApiResponse createFeedback(@RequestBody FeedbackRequest request) { + Feedback feedback = new Feedback(); + feedback.setFamilyId(Long.valueOf(StpUtil.getLoginId().toString())); + feedback.setElderId(request.getElderId()); + feedback.setType(request.getType()); + feedback.setContent(request.getContent()); + feedback.setRating(request.getRating()); + feedback.setStatus("NEW"); + feedback.setCreatedAt(LocalDateTime.now()); + feedbackMapper.insert(feedback); + return ApiResponse.success(feedback); + } + + @GetMapping("/notices") + public ApiResponse> listNotices() { + Long familyId = Long.valueOf(StpUtil.getLoginId().toString()); + return ApiResponse.success(noticeMapper.listByTarget("FAMILY", familyId)); + } +} diff --git a/backend/src/main/java/com/nursinghome/controller/FileController.java b/backend/src/main/java/com/nursinghome/controller/FileController.java new file mode 100644 index 0000000..ca48153 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/controller/FileController.java @@ -0,0 +1,42 @@ +package com.nursinghome.controller; + +import com.nursinghome.common.ApiResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@RestController +@RequestMapping("/api/files") +public class FileController { + @Value("${app.upload-dir}") + private String uploadDir; + + @PostMapping("/upload") + public ApiResponse> upload(@RequestParam("file") MultipartFile file) throws IOException { + if (file.isEmpty()) { + return ApiResponse.error("empty file"); + } + String original = file.getOriginalFilename(); + String ext = StringUtils.getFilenameExtension(original); + String name = UUID.randomUUID().toString().replace("-", ""); + if (ext != null && !ext.isEmpty()) { + name = name + "." + ext; + } + File dir = new File(uploadDir); + if (!dir.exists()) { + dir.mkdirs(); + } + File dest = new File(dir, name); + file.transferTo(dest); + Map data = new HashMap<>(); + data.put("url", "/files/" + name); + return ApiResponse.success(data); + } +} diff --git a/backend/src/main/java/com/nursinghome/controller/NurseController.java b/backend/src/main/java/com/nursinghome/controller/NurseController.java new file mode 100644 index 0000000..a8c4d35 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/controller/NurseController.java @@ -0,0 +1,101 @@ +package com.nursinghome.controller; + +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.stp.StpUtil; +import com.nursinghome.common.ApiResponse; +import com.nursinghome.dto.CareRecordRequest; +import com.nursinghome.dto.HandoverRequest; +import com.nursinghome.dto.HealthRecordRequest; +import com.nursinghome.entity.*; +import com.nursinghome.mapper.*; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/nurse") +@SaCheckRole("NURSE") +public class NurseController { + private final ScheduleMapper scheduleMapper; + private final CareRecordMapper careRecordMapper; + private final HealthRecordMapper healthRecordMapper; + private final HandoverMapper handoverMapper; + private final NoticeMapper noticeMapper; + + public NurseController(ScheduleMapper scheduleMapper, + CareRecordMapper careRecordMapper, + HealthRecordMapper healthRecordMapper, + HandoverMapper handoverMapper, + NoticeMapper noticeMapper) { + this.scheduleMapper = scheduleMapper; + this.careRecordMapper = careRecordMapper; + this.healthRecordMapper = healthRecordMapper; + this.handoverMapper = handoverMapper; + this.noticeMapper = noticeMapper; + } + + @GetMapping("/schedules") + public ApiResponse> listSchedules(@RequestParam String date) { + Long nurseId = Long.valueOf(StpUtil.getLoginId().toString()); + return ApiResponse.success(scheduleMapper.listByNurseAndDate(nurseId, java.time.LocalDate.parse(date))); + } + + @PostMapping("/care-records") + public ApiResponse createCare(@RequestBody CareRecordRequest request) { + CareRecord record = new CareRecord(); + record.setElderId(request.getElderId()); + record.setNurseId(Long.valueOf(StpUtil.getLoginId().toString())); + record.setContent(request.getContent()); + record.setAttachmentUrl(request.getAttachmentUrl()); + record.setRecordTime(request.getRecordTime() == null ? java.time.LocalDateTime.now() : request.getRecordTime()); + careRecordMapper.insert(record); + return ApiResponse.success(record); + } + + @GetMapping("/care-records") + public ApiResponse> listCare(@RequestParam Long elderId) { + return ApiResponse.success(careRecordMapper.listByElderId(elderId)); + } + + @PostMapping("/health-records") + public ApiResponse createHealth(@RequestBody HealthRecordRequest request) { + HealthRecord record = new HealthRecord(); + record.setElderId(request.getElderId()); + record.setNurseId(Long.valueOf(StpUtil.getLoginId().toString())); + record.setTemperature(request.getTemperature()); + record.setBpSystolic(request.getBpSystolic()); + record.setBpDiastolic(request.getBpDiastolic()); + record.setHeartRate(request.getHeartRate()); + record.setNote(request.getNote()); + record.setRecordTime(request.getRecordTime() == null ? java.time.LocalDateTime.now() : request.getRecordTime()); + healthRecordMapper.insert(record); + return ApiResponse.success(record); + } + + @GetMapping("/health-records") + public ApiResponse> listHealth(@RequestParam Long elderId) { + return ApiResponse.success(healthRecordMapper.listByElderId(elderId)); + } + + @PostMapping("/handovers") + public ApiResponse createHandover(@RequestBody HandoverRequest request) { + Handover handover = new Handover(); + handover.setNurseId(Long.valueOf(StpUtil.getLoginId().toString())); + handover.setDate(request.getDate()); + handover.setContent(request.getContent()); + handoverMapper.insert(handover); + return ApiResponse.success(handover); + } + + @GetMapping("/handovers") + public ApiResponse> listHandovers() { + Long nurseId = Long.valueOf(StpUtil.getLoginId().toString()); + return ApiResponse.success(handoverMapper.listByNurseId(nurseId)); + } + + @GetMapping("/notices") + public ApiResponse> listNotices() { + Long nurseId = Long.valueOf(StpUtil.getLoginId().toString()); + return ApiResponse.success(noticeMapper.listByTarget("NURSE", nurseId)); + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/BillRequest.java b/backend/src/main/java/com/nursinghome/dto/BillRequest.java new file mode 100644 index 0000000..ec05fa5 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/BillRequest.java @@ -0,0 +1,60 @@ +package com.nursinghome.dto; + +import java.math.BigDecimal; + +public class BillRequest { + private Long elderId; + private String month; + private BigDecimal bedFee; + private BigDecimal careFee; + private BigDecimal mealFee; + private BigDecimal otherFee; + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getMonth() { + return month; + } + + public void setMonth(String month) { + this.month = month; + } + + public BigDecimal getBedFee() { + return bedFee; + } + + public void setBedFee(BigDecimal bedFee) { + this.bedFee = bedFee; + } + + public BigDecimal getCareFee() { + return careFee; + } + + public void setCareFee(BigDecimal careFee) { + this.careFee = careFee; + } + + public BigDecimal getMealFee() { + return mealFee; + } + + public void setMealFee(BigDecimal mealFee) { + this.mealFee = mealFee; + } + + public BigDecimal getOtherFee() { + return otherFee; + } + + public void setOtherFee(BigDecimal otherFee) { + this.otherFee = otherFee; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/CareRecordRequest.java b/backend/src/main/java/com/nursinghome/dto/CareRecordRequest.java new file mode 100644 index 0000000..fe3a3c8 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/CareRecordRequest.java @@ -0,0 +1,42 @@ +package com.nursinghome.dto; + +import java.time.LocalDateTime; + +public class CareRecordRequest { + private Long elderId; + private String content; + private String attachmentUrl; + private LocalDateTime recordTime; + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getAttachmentUrl() { + return attachmentUrl; + } + + public void setAttachmentUrl(String attachmentUrl) { + this.attachmentUrl = attachmentUrl; + } + + public LocalDateTime getRecordTime() { + return recordTime; + } + + public void setRecordTime(LocalDateTime recordTime) { + this.recordTime = recordTime; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/ElderRequest.java b/backend/src/main/java/com/nursinghome/dto/ElderRequest.java new file mode 100644 index 0000000..a04ee47 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/ElderRequest.java @@ -0,0 +1,96 @@ +package com.nursinghome.dto; + +import java.time.LocalDate; + +public class ElderRequest { + private Long id; + private String name; + private String gender; + private String idCard; + private LocalDate birthday; + private String roomNo; + private LocalDate checkInDate; + private String careLevel; + private String status; + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getIdCard() { + return idCard; + } + + public void setIdCard(String idCard) { + this.idCard = idCard; + } + + public java.time.LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(java.time.LocalDate birthday) { + this.birthday = birthday; + } + + public String getRoomNo() { + return roomNo; + } + + public void setRoomNo(String roomNo) { + this.roomNo = roomNo; + } + + public java.time.LocalDate getCheckInDate() { + return checkInDate; + } + + public void setCheckInDate(java.time.LocalDate checkInDate) { + this.checkInDate = checkInDate; + } + + public String getCareLevel() { + return careLevel; + } + + public void setCareLevel(String careLevel) { + this.careLevel = careLevel; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/FeedbackRequest.java b/backend/src/main/java/com/nursinghome/dto/FeedbackRequest.java new file mode 100644 index 0000000..2d29e29 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/FeedbackRequest.java @@ -0,0 +1,40 @@ +package com.nursinghome.dto; + +public class FeedbackRequest { + private Long elderId; + private String type; + private String content; + private Integer rating; + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getRating() { + return rating; + } + + public void setRating(Integer rating) { + this.rating = rating; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/FeedbackUpdateRequest.java b/backend/src/main/java/com/nursinghome/dto/FeedbackUpdateRequest.java new file mode 100644 index 0000000..89861b9 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/FeedbackUpdateRequest.java @@ -0,0 +1,31 @@ +package com.nursinghome.dto; + +public class FeedbackUpdateRequest { + private Long id; + private String status; + private String reply; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getReply() { + return reply; + } + + public void setReply(String reply) { + this.reply = reply; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/HandoverRequest.java b/backend/src/main/java/com/nursinghome/dto/HandoverRequest.java new file mode 100644 index 0000000..45e416a --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/HandoverRequest.java @@ -0,0 +1,24 @@ +package com.nursinghome.dto; + +import java.time.LocalDate; + +public class HandoverRequest { + private LocalDate date; + private String content; + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/HealthRecordRequest.java b/backend/src/main/java/com/nursinghome/dto/HealthRecordRequest.java new file mode 100644 index 0000000..d642948 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/HealthRecordRequest.java @@ -0,0 +1,70 @@ +package com.nursinghome.dto; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class HealthRecordRequest { + private Long elderId; + private BigDecimal temperature; + private Integer bpSystolic; + private Integer bpDiastolic; + private Integer heartRate; + private String note; + private LocalDateTime recordTime; + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public BigDecimal getTemperature() { + return temperature; + } + + public void setTemperature(BigDecimal temperature) { + this.temperature = temperature; + } + + public Integer getBpSystolic() { + return bpSystolic; + } + + public void setBpSystolic(Integer bpSystolic) { + this.bpSystolic = bpSystolic; + } + + public Integer getBpDiastolic() { + return bpDiastolic; + } + + public void setBpDiastolic(Integer bpDiastolic) { + this.bpDiastolic = bpDiastolic; + } + + public Integer getHeartRate() { + return heartRate; + } + + public void setHeartRate(Integer heartRate) { + this.heartRate = heartRate; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public LocalDateTime getRecordTime() { + return recordTime; + } + + public void setRecordTime(LocalDateTime recordTime) { + this.recordTime = recordTime; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/LoginRequest.java b/backend/src/main/java/com/nursinghome/dto/LoginRequest.java new file mode 100644 index 0000000..694df6d --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/LoginRequest.java @@ -0,0 +1,26 @@ +package com.nursinghome.dto; + +import jakarta.validation.constraints.NotBlank; + +public class LoginRequest { + @NotBlank(message = "username required") + private String username; + @NotBlank(message = "password required") + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/NoticeRequest.java b/backend/src/main/java/com/nursinghome/dto/NoticeRequest.java new file mode 100644 index 0000000..b4fa26f --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/NoticeRequest.java @@ -0,0 +1,40 @@ +package com.nursinghome.dto; + +public class NoticeRequest { + private String title; + private String content; + private String targetRole; + private Long targetUserId; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getTargetRole() { + return targetRole; + } + + public void setTargetRole(String targetRole) { + this.targetRole = targetRole; + } + + public Long getTargetUserId() { + return targetUserId; + } + + public void setTargetUserId(Long targetUserId) { + this.targetUserId = targetUserId; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/PayRequest.java b/backend/src/main/java/com/nursinghome/dto/PayRequest.java new file mode 100644 index 0000000..08f2b90 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/PayRequest.java @@ -0,0 +1,13 @@ +package com.nursinghome.dto; + +public class PayRequest { + private String method; + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/RegisterRequest.java b/backend/src/main/java/com/nursinghome/dto/RegisterRequest.java new file mode 100644 index 0000000..fa48de2 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/RegisterRequest.java @@ -0,0 +1,64 @@ +package com.nursinghome.dto; + +import jakarta.validation.constraints.NotBlank; + +public class RegisterRequest { + @NotBlank(message = "username required") + private String username; + @NotBlank(message = "password required") + private String password; + @NotBlank(message = "name required") + private String name; + private String phone; + @NotBlank(message = "elderIdCard required") + private String elderIdCard; + private String relationship; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getElderIdCard() { + return elderIdCard; + } + + public void setElderIdCard(String elderIdCard) { + this.elderIdCard = elderIdCard; + } + + public String getRelationship() { + return relationship; + } + + public void setRelationship(String relationship) { + this.relationship = relationship; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/ScheduleRequest.java b/backend/src/main/java/com/nursinghome/dto/ScheduleRequest.java new file mode 100644 index 0000000..74a38c0 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/ScheduleRequest.java @@ -0,0 +1,51 @@ +package com.nursinghome.dto; + +import java.time.LocalDate; + +public class ScheduleRequest { + private Long id; + private Long nurseId; + private LocalDate date; + private String shift; + private String task; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getNurseId() { + return nurseId; + } + + public void setNurseId(Long nurseId) { + this.nurseId = nurseId; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getShift() { + return shift; + } + + public void setShift(String shift) { + this.shift = shift; + } + + public String getTask() { + return task; + } + + public void setTask(String task) { + this.task = task; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/UserRequest.java b/backend/src/main/java/com/nursinghome/dto/UserRequest.java new file mode 100644 index 0000000..0ff9ef2 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/UserRequest.java @@ -0,0 +1,58 @@ +package com.nursinghome.dto; + +public class UserRequest { + private String username; + private String password; + private String name; + private String phone; + private String role; + private Integer status; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } +} diff --git a/backend/src/main/java/com/nursinghome/dto/UserUpdateRequest.java b/backend/src/main/java/com/nursinghome/dto/UserUpdateRequest.java new file mode 100644 index 0000000..8bfc363 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/dto/UserUpdateRequest.java @@ -0,0 +1,40 @@ +package com.nursinghome.dto; + +public class UserUpdateRequest { + private Long id; + private String name; + private String phone; + private Integer status; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Bill.java b/backend/src/main/java/com/nursinghome/entity/Bill.java new file mode 100644 index 0000000..bac592b --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Bill.java @@ -0,0 +1,106 @@ +package com.nursinghome.entity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class Bill { + private Long id; + private Long elderId; + private String month; + private BigDecimal bedFee; + private BigDecimal careFee; + private BigDecimal mealFee; + private BigDecimal otherFee; + private BigDecimal total; + private String status; + private LocalDateTime createdAt; + private LocalDateTime paidAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getMonth() { + return month; + } + + public void setMonth(String month) { + this.month = month; + } + + public BigDecimal getBedFee() { + return bedFee; + } + + public void setBedFee(BigDecimal bedFee) { + this.bedFee = bedFee; + } + + public BigDecimal getCareFee() { + return careFee; + } + + public void setCareFee(BigDecimal careFee) { + this.careFee = careFee; + } + + public BigDecimal getMealFee() { + return mealFee; + } + + public void setMealFee(BigDecimal mealFee) { + this.mealFee = mealFee; + } + + public BigDecimal getOtherFee() { + return otherFee; + } + + public void setOtherFee(BigDecimal otherFee) { + this.otherFee = otherFee; + } + + public BigDecimal getTotal() { + return total; + } + + public void setTotal(BigDecimal total) { + this.total = total; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getPaidAt() { + return paidAt; + } + + public void setPaidAt(LocalDateTime paidAt) { + this.paidAt = paidAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/CareRecord.java b/backend/src/main/java/com/nursinghome/entity/CareRecord.java new file mode 100644 index 0000000..4336772 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/CareRecord.java @@ -0,0 +1,69 @@ +package com.nursinghome.entity; + +import java.time.LocalDateTime; + +public class CareRecord { + private Long id; + private Long elderId; + private Long nurseId; + private String content; + private String attachmentUrl; + private LocalDateTime recordTime; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public Long getNurseId() { + return nurseId; + } + + public void setNurseId(Long nurseId) { + this.nurseId = nurseId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getAttachmentUrl() { + return attachmentUrl; + } + + public void setAttachmentUrl(String attachmentUrl) { + this.attachmentUrl = attachmentUrl; + } + + public LocalDateTime getRecordTime() { + return recordTime; + } + + public void setRecordTime(LocalDateTime recordTime) { + this.recordTime = recordTime; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Elder.java b/backend/src/main/java/com/nursinghome/entity/Elder.java new file mode 100644 index 0000000..537d704 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Elder.java @@ -0,0 +1,96 @@ +package com.nursinghome.entity; + +import java.time.LocalDate; + +public class Elder { + private Long id; + private String name; + private String gender; + private String idCard; + private LocalDate birthday; + private String roomNo; + private LocalDate checkInDate; + private String careLevel; + private String status; + private String remark; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getIdCard() { + return idCard; + } + + public void setIdCard(String idCard) { + this.idCard = idCard; + } + + public LocalDate getBirthday() { + return birthday; + } + + public void setBirthday(LocalDate birthday) { + this.birthday = birthday; + } + + public String getRoomNo() { + return roomNo; + } + + public void setRoomNo(String roomNo) { + this.roomNo = roomNo; + } + + public LocalDate getCheckInDate() { + return checkInDate; + } + + public void setCheckInDate(LocalDate checkInDate) { + this.checkInDate = checkInDate; + } + + public String getCareLevel() { + return careLevel; + } + + public void setCareLevel(String careLevel) { + this.careLevel = careLevel; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/FamilyElder.java b/backend/src/main/java/com/nursinghome/entity/FamilyElder.java new file mode 100644 index 0000000..f76f8d1 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/FamilyElder.java @@ -0,0 +1,51 @@ +package com.nursinghome.entity; + +import java.time.LocalDateTime; + +public class FamilyElder { + private Long id; + private Long familyId; + private Long elderId; + private String relationship; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getFamilyId() { + return familyId; + } + + public void setFamilyId(Long familyId) { + this.familyId = familyId; + } + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getRelationship() { + return relationship; + } + + public void setRelationship(String relationship) { + this.relationship = relationship; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Feedback.java b/backend/src/main/java/com/nursinghome/entity/Feedback.java new file mode 100644 index 0000000..a860343 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Feedback.java @@ -0,0 +1,96 @@ +package com.nursinghome.entity; + +import java.time.LocalDateTime; + +public class Feedback { + private Long id; + private Long familyId; + private Long elderId; + private String type; + private String content; + private Integer rating; + private String status; + private String reply; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getFamilyId() { + return familyId; + } + + public void setFamilyId(Long familyId) { + this.familyId = familyId; + } + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getRating() { + return rating; + } + + public void setRating(Integer rating) { + this.rating = rating; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getReply() { + return reply; + } + + public void setReply(String reply) { + this.reply = reply; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Handover.java b/backend/src/main/java/com/nursinghome/entity/Handover.java new file mode 100644 index 0000000..6a36933 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Handover.java @@ -0,0 +1,52 @@ +package com.nursinghome.entity; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class Handover { + private Long id; + private Long nurseId; + private LocalDate date; + private String content; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getNurseId() { + return nurseId; + } + + public void setNurseId(Long nurseId) { + this.nurseId = nurseId; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/HealthRecord.java b/backend/src/main/java/com/nursinghome/entity/HealthRecord.java new file mode 100644 index 0000000..32aa0b8 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/HealthRecord.java @@ -0,0 +1,97 @@ +package com.nursinghome.entity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class HealthRecord { + private Long id; + private Long elderId; + private Long nurseId; + private BigDecimal temperature; + private Integer bpSystolic; + private Integer bpDiastolic; + private Integer heartRate; + private String note; + private LocalDateTime recordTime; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getElderId() { + return elderId; + } + + public void setElderId(Long elderId) { + this.elderId = elderId; + } + + public Long getNurseId() { + return nurseId; + } + + public void setNurseId(Long nurseId) { + this.nurseId = nurseId; + } + + public BigDecimal getTemperature() { + return temperature; + } + + public void setTemperature(BigDecimal temperature) { + this.temperature = temperature; + } + + public Integer getBpSystolic() { + return bpSystolic; + } + + public void setBpSystolic(Integer bpSystolic) { + this.bpSystolic = bpSystolic; + } + + public Integer getBpDiastolic() { + return bpDiastolic; + } + + public void setBpDiastolic(Integer bpDiastolic) { + this.bpDiastolic = bpDiastolic; + } + + public Integer getHeartRate() { + return heartRate; + } + + public void setHeartRate(Integer heartRate) { + this.heartRate = heartRate; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public LocalDateTime getRecordTime() { + return recordTime; + } + + public void setRecordTime(LocalDateTime recordTime) { + this.recordTime = recordTime; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Notice.java b/backend/src/main/java/com/nursinghome/entity/Notice.java new file mode 100644 index 0000000..fac03fa --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Notice.java @@ -0,0 +1,69 @@ +package com.nursinghome.entity; + +import java.time.LocalDateTime; + +public class Notice { + private Long id; + private String title; + private String content; + private String targetRole; + private Long targetUserId; + private Long createdBy; + private LocalDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getTargetRole() { + return targetRole; + } + + public void setTargetRole(String targetRole) { + this.targetRole = targetRole; + } + + public Long getTargetUserId() { + return targetUserId; + } + + public void setTargetUserId(Long targetUserId) { + this.targetUserId = targetUserId; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/PaymentRecord.java b/backend/src/main/java/com/nursinghome/entity/PaymentRecord.java new file mode 100644 index 0000000..37e6783 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/PaymentRecord.java @@ -0,0 +1,61 @@ +package com.nursinghome.entity; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class PaymentRecord { + private Long id; + private Long billId; + private Long familyId; + private BigDecimal amount; + private String method; + private LocalDateTime paidAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getBillId() { + return billId; + } + + public void setBillId(Long billId) { + this.billId = billId; + } + + public Long getFamilyId() { + return familyId; + } + + public void setFamilyId(Long familyId) { + this.familyId = familyId; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public LocalDateTime getPaidAt() { + return paidAt; + } + + public void setPaidAt(LocalDateTime paidAt) { + this.paidAt = paidAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/Schedule.java b/backend/src/main/java/com/nursinghome/entity/Schedule.java new file mode 100644 index 0000000..51a3a69 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/Schedule.java @@ -0,0 +1,70 @@ +package com.nursinghome.entity; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class Schedule { + private Long id; + private Long nurseId; + private LocalDate date; + private String shift; + private String task; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getNurseId() { + return nurseId; + } + + public void setNurseId(Long nurseId) { + this.nurseId = nurseId; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public String getShift() { + return shift; + } + + public void setShift(String shift) { + this.shift = shift; + } + + public String getTask() { + return task; + } + + public void setTask(String task) { + this.task = task; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/entity/User.java b/backend/src/main/java/com/nursinghome/entity/User.java new file mode 100644 index 0000000..9baacac --- /dev/null +++ b/backend/src/main/java/com/nursinghome/entity/User.java @@ -0,0 +1,87 @@ +package com.nursinghome.entity; + +import java.time.LocalDateTime; + +public class User { + private Long id; + private String username; + private String password; + private String name; + private String phone; + private String role; + private Integer status; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/backend/src/main/java/com/nursinghome/mapper/BillMapper.java b/backend/src/main/java/com/nursinghome/mapper/BillMapper.java new file mode 100644 index 0000000..044d248 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/BillMapper.java @@ -0,0 +1,28 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Bill; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface BillMapper { + @Insert("INSERT INTO bill(elder_id, month, bed_fee, care_fee, meal_fee, other_fee, total, status, created_at) VALUES(#{elderId}, #{month}, #{bedFee}, #{careFee}, #{mealFee}, #{otherFee}, #{total}, #{status}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Bill bill); + + @Select("SELECT * FROM bill ORDER BY month DESC") + List listAll(); + + @Select("SELECT * FROM bill WHERE id = #{id}") + Bill findById(Long id); + + @Select("SELECT * FROM bill WHERE elder_id = #{elderId} ORDER BY month DESC") + List listByElderId(Long elderId); + + @Select("") + List listByElderIds(@Param("elderIds") List elderIds); + + @Update("UPDATE bill SET status=#{status}, paid_at=#{paidAt} WHERE id=#{id}") + int updateStatus(Bill bill); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/CareRecordMapper.java b/backend/src/main/java/com/nursinghome/mapper/CareRecordMapper.java new file mode 100644 index 0000000..7719a39 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/CareRecordMapper.java @@ -0,0 +1,19 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.CareRecord; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface CareRecordMapper { + @Insert("INSERT INTO care_record(elder_id, nurse_id, content, attachment_url, record_time, created_at) VALUES(#{elderId}, #{nurseId}, #{content}, #{attachmentUrl}, #{recordTime}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(CareRecord record); + + @Select("SELECT * FROM care_record WHERE elder_id = #{elderId} ORDER BY record_time DESC") + List listByElderId(Long elderId); + + @Select("") + List listByElderIds(@Param("elderIds") List elderIds); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/ElderMapper.java b/backend/src/main/java/com/nursinghome/mapper/ElderMapper.java new file mode 100644 index 0000000..475c226 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/ElderMapper.java @@ -0,0 +1,28 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Elder; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface ElderMapper { + @Select("SELECT * FROM elder ORDER BY id DESC") + List listAll(); + + @Select("SELECT * FROM elder WHERE id = #{id}") + Elder findById(Long id); + + @Select("SELECT * FROM elder WHERE id_card = #{idCard} LIMIT 1") + Elder findByIdCard(String idCard); + + @Insert("INSERT INTO elder(name, gender, id_card, birthday, room_no, check_in_date, care_level, status, remark) VALUES(#{name}, #{gender}, #{idCard}, #{birthday}, #{roomNo}, #{checkInDate}, #{careLevel}, #{status}, #{remark})") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Elder elder); + + @Update("UPDATE elder SET name=#{name}, gender=#{gender}, id_card=#{idCard}, birthday=#{birthday}, room_no=#{roomNo}, check_in_date=#{checkInDate}, care_level=#{careLevel}, status=#{status}, remark=#{remark} WHERE id=#{id}") + int update(Elder elder); + + @Delete("DELETE FROM elder WHERE id=#{id}") + int delete(Long id); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/FamilyElderMapper.java b/backend/src/main/java/com/nursinghome/mapper/FamilyElderMapper.java new file mode 100644 index 0000000..52670ba --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/FamilyElderMapper.java @@ -0,0 +1,19 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.FamilyElder; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface FamilyElderMapper { + @Insert("INSERT INTO family_elder(family_id, elder_id, relationship, created_at) VALUES(#{familyId}, #{elderId}, #{relationship}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(FamilyElder relation); + + @Select("SELECT * FROM family_elder WHERE family_id = #{familyId}") + List listByFamilyId(Long familyId); + + @Select("SELECT * FROM family_elder WHERE family_id = #{familyId} AND elder_id = #{elderId} LIMIT 1") + FamilyElder findByFamilyAndElder(@Param("familyId") Long familyId, @Param("elderId") Long elderId); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/FeedbackMapper.java b/backend/src/main/java/com/nursinghome/mapper/FeedbackMapper.java new file mode 100644 index 0000000..0e197c3 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/FeedbackMapper.java @@ -0,0 +1,19 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Feedback; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface FeedbackMapper { + @Insert("INSERT INTO feedback(family_id, elder_id, type, content, rating, status, created_at, updated_at) VALUES(#{familyId}, #{elderId}, #{type}, #{content}, #{rating}, #{status}, NOW(), NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Feedback feedback); + + @Select("SELECT * FROM feedback ORDER BY created_at DESC") + List listAll(); + + @Update("UPDATE feedback SET status=#{status}, reply=#{reply}, updated_at=NOW() WHERE id=#{id}") + int updateStatus(Feedback feedback); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/HandoverMapper.java b/backend/src/main/java/com/nursinghome/mapper/HandoverMapper.java new file mode 100644 index 0000000..59f530f --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/HandoverMapper.java @@ -0,0 +1,16 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Handover; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface HandoverMapper { + @Insert("INSERT INTO handover(nurse_id, date, content, created_at) VALUES(#{nurseId}, #{date}, #{content}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Handover handover); + + @Select("SELECT * FROM handover WHERE nurse_id = #{nurseId} ORDER BY date DESC") + List listByNurseId(Long nurseId); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/HealthRecordMapper.java b/backend/src/main/java/com/nursinghome/mapper/HealthRecordMapper.java new file mode 100644 index 0000000..688d0a6 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/HealthRecordMapper.java @@ -0,0 +1,19 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.HealthRecord; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface HealthRecordMapper { + @Insert("INSERT INTO health_record(elder_id, nurse_id, temperature, bp_systolic, bp_diastolic, heart_rate, note, record_time, created_at) VALUES(#{elderId}, #{nurseId}, #{temperature}, #{bpSystolic}, #{bpDiastolic}, #{heartRate}, #{note}, #{recordTime}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(HealthRecord record); + + @Select("SELECT * FROM health_record WHERE elder_id = #{elderId} ORDER BY record_time DESC") + List listByElderId(Long elderId); + + @Select("") + List listByElderIds(@Param("elderIds") List elderIds); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/NoticeMapper.java b/backend/src/main/java/com/nursinghome/mapper/NoticeMapper.java new file mode 100644 index 0000000..d850d09 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/NoticeMapper.java @@ -0,0 +1,16 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Notice; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface NoticeMapper { + @Insert("INSERT INTO notice(title, content, target_role, target_user_id, created_by, created_at) VALUES(#{title}, #{content}, #{targetRole}, #{targetUserId}, #{createdBy}, NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Notice notice); + + @Select("") + List listByTarget(@Param("targetRole") String targetRole, @Param("targetUserId") Long targetUserId); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/PaymentRecordMapper.java b/backend/src/main/java/com/nursinghome/mapper/PaymentRecordMapper.java new file mode 100644 index 0000000..6731892 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/PaymentRecordMapper.java @@ -0,0 +1,16 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.PaymentRecord; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface PaymentRecordMapper { + @Insert("INSERT INTO payment_record(bill_id, family_id, amount, method, paid_at) VALUES(#{billId}, #{familyId}, #{amount}, #{method}, #{paidAt})") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(PaymentRecord record); + + @Select("SELECT * FROM payment_record WHERE bill_id = #{billId} ORDER BY paid_at DESC") + List listByBillId(Long billId); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/ScheduleMapper.java b/backend/src/main/java/com/nursinghome/mapper/ScheduleMapper.java new file mode 100644 index 0000000..0f7b5b3 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/ScheduleMapper.java @@ -0,0 +1,26 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.Schedule; +import org.apache.ibatis.annotations.*; + +import java.time.LocalDate; +import java.util.List; + +@Mapper +public interface ScheduleMapper { + @Select("SELECT * FROM schedule WHERE nurse_id = #{nurseId} AND date = #{date} ORDER BY id DESC") + List listByNurseAndDate(@Param("nurseId") Long nurseId, @Param("date") LocalDate date); + + @Select("SELECT * FROM schedule WHERE date = #{date} ORDER BY id DESC") + List listByDate(LocalDate date); + + @Insert("INSERT INTO schedule(nurse_id, date, shift, task, created_at, updated_at) VALUES(#{nurseId}, #{date}, #{shift}, #{task}, NOW(), NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(Schedule schedule); + + @Update("UPDATE schedule SET nurse_id=#{nurseId}, date=#{date}, shift=#{shift}, task=#{task}, updated_at=NOW() WHERE id=#{id}") + int update(Schedule schedule); + + @Delete("DELETE FROM schedule WHERE id=#{id}") + int delete(Long id); +} diff --git a/backend/src/main/java/com/nursinghome/mapper/UserMapper.java b/backend/src/main/java/com/nursinghome/mapper/UserMapper.java new file mode 100644 index 0000000..e3787db --- /dev/null +++ b/backend/src/main/java/com/nursinghome/mapper/UserMapper.java @@ -0,0 +1,31 @@ +package com.nursinghome.mapper; + +import com.nursinghome.entity.User; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +@Mapper +public interface UserMapper { + @Select("SELECT * FROM sys_user WHERE username = #{username} LIMIT 1") + User findByUsername(String username); + + @Select("SELECT * FROM sys_user WHERE id = #{id}") + User findById(Long id); + + @Select("") + List listByRole(@Param("role") String role); + + @Insert("INSERT INTO sys_user(username, password, name, phone, role, status, created_at, updated_at) VALUES(#{username}, #{password}, #{name}, #{phone}, #{role}, #{status}, NOW(), NOW())") + @Options(useGeneratedKeys = true, keyProperty = "id") + int insert(User user); + + @Update("UPDATE sys_user SET name=#{name}, phone=#{phone}, status=#{status}, updated_at=NOW() WHERE id=#{id}") + int update(User user); + + @Update("UPDATE sys_user SET password=#{password}, updated_at=NOW() WHERE id=#{id}") + int updatePassword(@Param("id") Long id, @Param("password") String password); + + @Update("UPDATE sys_user SET status=#{status}, updated_at=NOW() WHERE id=#{id}") + int updateStatus(@Param("id") Long id, @Param("status") Integer status); +} diff --git a/backend/src/main/java/com/nursinghome/service/BillService.java b/backend/src/main/java/com/nursinghome/service/BillService.java new file mode 100644 index 0000000..b20ba6d --- /dev/null +++ b/backend/src/main/java/com/nursinghome/service/BillService.java @@ -0,0 +1,62 @@ +package com.nursinghome.service; + +import com.nursinghome.entity.Bill; +import com.nursinghome.entity.PaymentRecord; +import com.nursinghome.mapper.BillMapper; +import com.nursinghome.mapper.PaymentRecordMapper; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class BillService { + private final BillMapper billMapper; + private final PaymentRecordMapper paymentRecordMapper; + + public BillService(BillMapper billMapper, PaymentRecordMapper paymentRecordMapper) { + this.billMapper = billMapper; + this.paymentRecordMapper = paymentRecordMapper; + } + + public Bill createBill(Bill bill) { + if (bill.getTotal() == null) { + BigDecimal total = BigDecimal.ZERO; + if (bill.getBedFee() != null) total = total.add(bill.getBedFee()); + if (bill.getCareFee() != null) total = total.add(bill.getCareFee()); + if (bill.getMealFee() != null) total = total.add(bill.getMealFee()); + if (bill.getOtherFee() != null) total = total.add(bill.getOtherFee()); + bill.setTotal(total); + } + if (bill.getStatus() == null) { + bill.setStatus("UNPAID"); + } + billMapper.insert(bill); + return bill; + } + + public List listByElderId(Long elderId) { + return billMapper.listByElderId(elderId); + } + + public List listByElderIds(List elderIds) { + return billMapper.listByElderIds(elderIds); + } + + public void payBill(Long billId, Long familyId, String method, BigDecimal amount) { + Bill bill = new Bill(); + bill.setId(billId); + bill.setStatus("PAID"); + bill.setPaidAt(LocalDateTime.now()); + billMapper.updateStatus(bill); + + PaymentRecord record = new PaymentRecord(); + record.setBillId(billId); + record.setFamilyId(familyId); + record.setAmount(amount); + record.setMethod(method); + record.setPaidAt(LocalDateTime.now()); + paymentRecordMapper.insert(record); + } +} diff --git a/backend/src/main/java/com/nursinghome/service/ElderService.java b/backend/src/main/java/com/nursinghome/service/ElderService.java new file mode 100644 index 0000000..e2ec540 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/service/ElderService.java @@ -0,0 +1,45 @@ +package com.nursinghome.service; + +import com.nursinghome.common.ApiException; +import com.nursinghome.entity.Elder; +import com.nursinghome.mapper.ElderMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ElderService { + private final ElderMapper elderMapper; + + public ElderService(ElderMapper elderMapper) { + this.elderMapper = elderMapper; + } + + public List listAll() { + return elderMapper.listAll(); + } + + public Elder findById(Long id) { + return elderMapper.findById(id); + } + + public Elder findByIdCard(String idCard) { + return elderMapper.findByIdCard(idCard); + } + + public Elder create(Elder elder) { + if (elderMapper.findByIdCard(elder.getIdCard()) != null) { + throw new ApiException("id card already exists"); + } + elderMapper.insert(elder); + return elder; + } + + public void update(Elder elder) { + elderMapper.update(elder); + } + + public void delete(Long id) { + elderMapper.delete(id); + } +} diff --git a/backend/src/main/java/com/nursinghome/service/FamilyService.java b/backend/src/main/java/com/nursinghome/service/FamilyService.java new file mode 100644 index 0000000..23a004c --- /dev/null +++ b/backend/src/main/java/com/nursinghome/service/FamilyService.java @@ -0,0 +1,50 @@ +package com.nursinghome.service; + +import com.nursinghome.common.ApiException; +import com.nursinghome.entity.Elder; +import com.nursinghome.entity.FamilyElder; +import com.nursinghome.entity.User; +import com.nursinghome.mapper.FamilyElderMapper; +import com.nursinghome.util.PasswordUtil; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class FamilyService { + private final UserService userService; + private final ElderService elderService; + private final FamilyElderMapper familyElderMapper; + + public FamilyService(UserService userService, ElderService elderService, FamilyElderMapper familyElderMapper) { + this.userService = userService; + this.elderService = elderService; + this.familyElderMapper = familyElderMapper; + } + + public User registerFamily(String username, String password, String name, String phone, String elderIdCard, String relationship) { + Elder elder = elderService.findByIdCard(elderIdCard); + if (elder == null) { + throw new ApiException("elder not found"); + } + User user = new User(); + user.setUsername(username); + user.setPassword(password); + user.setName(name); + user.setPhone(phone); + user.setRole("FAMILY"); + user.setStatus(1); + userService.createUser(user, true); + + FamilyElder relation = new FamilyElder(); + relation.setFamilyId(user.getId()); + relation.setElderId(elder.getId()); + relation.setRelationship(relationship); + familyElderMapper.insert(relation); + return user; + } + + public List listRelations(Long familyId) { + return familyElderMapper.listByFamilyId(familyId); + } +} diff --git a/backend/src/main/java/com/nursinghome/service/UserService.java b/backend/src/main/java/com/nursinghome/service/UserService.java new file mode 100644 index 0000000..50e48d8 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/service/UserService.java @@ -0,0 +1,56 @@ +package com.nursinghome.service; + +import com.nursinghome.common.ApiException; +import com.nursinghome.entity.User; +import com.nursinghome.mapper.UserMapper; +import com.nursinghome.util.PasswordUtil; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class UserService { + private final UserMapper userMapper; + + public UserService(UserMapper userMapper) { + this.userMapper = userMapper; + } + + public User findByUsername(String username) { + return userMapper.findByUsername(username); + } + + public User findById(Long id) { + return userMapper.findById(id); + } + + public List listByRole(String role) { + return userMapper.listByRole(role); + } + + public User createUser(User user, boolean hashPassword) { + if (userMapper.findByUsername(user.getUsername()) != null) { + throw new ApiException("username already exists"); + } + if (hashPassword) { + user.setPassword(PasswordUtil.hash(user.getPassword())); + } + if (user.getStatus() == null) { + user.setStatus(1); + } + userMapper.insert(user); + return user; + } + + public void updateUser(User user) { + userMapper.update(user); + } + + public void updateStatus(Long id, Integer status) { + userMapper.updateStatus(id, status); + } + + public void updatePassword(Long id, String password) { + userMapper.updatePassword(id, password); + } +} diff --git a/backend/src/main/java/com/nursinghome/util/PasswordUtil.java b/backend/src/main/java/com/nursinghome/util/PasswordUtil.java new file mode 100644 index 0000000..8f00634 --- /dev/null +++ b/backend/src/main/java/com/nursinghome/util/PasswordUtil.java @@ -0,0 +1,25 @@ +package com.nursinghome.util; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class PasswordUtil { + private static final BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder(); + + public static String hash(String raw) { + return ENCODER.encode(raw); + } + + public static boolean matches(String raw, String stored) { + if (stored == null) { + return false; + } + if (isBcrypt(stored)) { + return ENCODER.matches(raw, stored); + } + return stored.equals(raw); + } + + public static boolean isBcrypt(String stored) { + return stored != null && stored.startsWith("$2"); + } +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml new file mode 100644 index 0000000..28d6dea --- /dev/null +++ b/backend/src/main/resources/application.yml @@ -0,0 +1,28 @@ +server: + port: 8080 + +spring: + datasource: + url: jdbc:mysql://localhost:3306/nursing_home?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai + username: root + password: root + driver-class-name: com.mysql.cj.jdbc.Driver + servlet: + multipart: + max-file-size: 10MB + max-request-size: 20MB + +mybatis: + configuration: + map-underscore-to-camel-case: true + +sa-token: + token-name: satoken + timeout: 86400 + activity-timeout: -1 + is-concurrent: true + is-share: true + token-style: uuid + +app: + upload-dir: uploads diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 0000000..678b15e --- /dev/null +++ b/frontend/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ["@vue/cli-plugin-babel/preset"] +}; diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..9fd2a10 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "nursing-home-frontend", + "version": "1.0.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build" + }, + "dependencies": { + "axios": "^1.6.8", + "element-ui": "^2.15.14", + "vue": "^2.7.16", + "vue-router": "^3.6.5" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "^5.0.8", + "@vue/cli-service": "^5.0.8", + "babel-core": "^7.0.0-bridge.0", + "babel-loader": "^8.3.0", + "vue-template-compiler": "^2.7.16" + } +} diff --git a/frontend/public/index.html b/frontend/public/index.html new file mode 100644 index 0000000..9c8ef07 --- /dev/null +++ b/frontend/public/index.html @@ -0,0 +1,14 @@ + + + + + + Nursing Home + + + +
+ + diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..df3a960 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,13 @@ + + + diff --git a/frontend/src/api/http.js b/frontend/src/api/http.js new file mode 100644 index 0000000..84888e0 --- /dev/null +++ b/frontend/src/api/http.js @@ -0,0 +1,26 @@ +import axios from "axios"; + +const http = axios.create({ + baseURL: "/api" +}); + +http.interceptors.request.use((config) => { + const token = localStorage.getItem("token"); + if (token) { + config.headers["satoken"] = token; + } + return config; +}); + +http.interceptors.response.use( + (response) => { + const data = response.data; + if (data && data.code !== 0) { + return Promise.reject(new Error(data.message || "request error")); + } + return response; + }, + (error) => Promise.reject(error) +); + +export default http; diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js new file mode 100644 index 0000000..dd8c061 --- /dev/null +++ b/frontend/src/api/index.js @@ -0,0 +1,54 @@ +import http from "./http"; + +export const login = (payload) => http.post("/auth/login", payload); +export const register = (payload) => http.post("/auth/register", payload); +export const me = () => http.get("/auth/me"); +export const logout = () => http.post("/auth/logout"); + +export const adminStats = () => http.get("/admin/stats"); +export const adminUsers = (role) => http.get("/admin/users", { params: { role } }); +export const adminCreateUser = (payload) => http.post("/admin/users", payload); +export const adminUpdateUser = (payload) => http.put("/admin/users", payload); +export const adminResetPassword = (id, password) => http.post(`/admin/users/${id}/reset-password`, null, { params: { password } }); + +export const eldersList = () => http.get("/admin/elders"); +export const eldersCreate = (payload) => http.post("/admin/elders", payload); +export const eldersUpdate = (payload) => http.put("/admin/elders", payload); +export const eldersDelete = (id) => http.delete(`/admin/elders/${id}`); + +export const schedulesByDate = (date) => http.get("/admin/schedules", { params: { date } }); +export const scheduleCreate = (payload) => http.post("/admin/schedules", payload); +export const scheduleUpdate = (payload) => http.put("/admin/schedules", payload); +export const scheduleDelete = (id) => http.delete(`/admin/schedules/${id}`); + +export const billsCreate = (payload) => http.post("/admin/bills", payload); +export const billsList = (elderId) => http.get("/admin/bills", { params: { elderId } }); + +export const feedbackList = () => http.get("/admin/feedback"); +export const feedbackUpdate = (payload) => http.put("/admin/feedback", payload); + +export const noticeCreate = (payload) => http.post("/admin/notices", payload); +export const noticeList = (role, userId) => http.get("/admin/notices", { params: { role, userId } }); + +export const nurseSchedules = (date) => http.get("/nurse/schedules", { params: { date } }); +export const nurseCareCreate = (payload) => http.post("/nurse/care-records", payload); +export const nurseCareList = (elderId) => http.get("/nurse/care-records", { params: { elderId } }); +export const nurseHealthCreate = (payload) => http.post("/nurse/health-records", payload); +export const nurseHealthList = (elderId) => http.get("/nurse/health-records", { params: { elderId } }); +export const nurseHandoverCreate = (payload) => http.post("/nurse/handovers", payload); +export const nurseHandoverList = () => http.get("/nurse/handovers"); +export const nurseNoticeList = () => http.get("/nurse/notices"); + +export const familyElders = () => http.get("/family/elders"); +export const familyCareList = (elderId) => http.get("/family/care-records", { params: { elderId } }); +export const familyHealthList = (elderId) => http.get("/family/health-records", { params: { elderId } }); +export const familyBills = (elderId) => http.get("/family/bills", { params: { elderId } }); +export const familyPay = (id, payload) => http.post(`/family/bills/${id}/pay`, payload); +export const familyFeedback = (payload) => http.post("/family/feedback", payload); +export const familyNoticeList = () => http.get("/family/notices"); + +export const uploadFile = (file) => { + const form = new FormData(); + form.append("file", file); + return http.post("/files/upload", form, { headers: { "Content-Type": "multipart/form-data" } }); +}; diff --git a/frontend/src/assets/theme.css b/frontend/src/assets/theme.css new file mode 100644 index 0000000..ab01d4c --- /dev/null +++ b/frontend/src/assets/theme.css @@ -0,0 +1,34 @@ +:root { + --primary-green: #2f7d59; + --primary-green-dark: #225c43; + --primary-green-light: #d9efe3; +} + +.el-button--primary, +.el-menu-item.is-active, +.el-submenu__title:hover, +.el-menu-item:hover { + background-color: var(--primary-green) !important; + border-color: var(--primary-green) !important; + color: #fff !important; +} + +.el-menu { + border-right: none; +} + +.page-card { + background: #fff; + border-radius: 10px; + padding: 16px; + box-shadow: 0 8px 24px rgba(47, 125, 89, 0.12); +} + +.header-bar { + background: linear-gradient(135deg, var(--primary-green) 0%, #5fbf90 100%); + color: #fff; + padding: 14px 20px; + display: flex; + align-items: center; + justify-content: space-between; +} diff --git a/frontend/src/main.js b/frontend/src/main.js new file mode 100644 index 0000000..a845671 --- /dev/null +++ b/frontend/src/main.js @@ -0,0 +1,14 @@ +import Vue from "vue"; +import ElementUI from "element-ui"; +import "element-ui/lib/theme-chalk/index.css"; +import "./assets/theme.css"; +import App from "./App.vue"; +import router from "./router"; + +Vue.use(ElementUI); +Vue.config.productionTip = false; + +new Vue({ + router, + render: (h) => h(App) +}).$mount("#app"); diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js new file mode 100644 index 0000000..14888d3 --- /dev/null +++ b/frontend/src/router/index.js @@ -0,0 +1,102 @@ +import Vue from "vue"; +import Router from "vue-router"; +import Login from "../views/Login.vue"; +import Register from "../views/Register.vue"; +import Layout from "../views/Layout.vue"; + +import AdminDashboard from "../views/admin/Dashboard.vue"; +import AdminUsers from "../views/admin/Users.vue"; +import AdminElders from "../views/admin/Elders.vue"; +import AdminSchedules from "../views/admin/Schedules.vue"; +import AdminBills from "../views/admin/Bills.vue"; +import AdminFeedback from "../views/admin/Feedback.vue"; +import AdminNotices from "../views/admin/Notices.vue"; + +import NurseDashboard from "../views/nurse/Dashboard.vue"; +import NurseSchedules from "../views/nurse/Schedules.vue"; +import NurseCare from "../views/nurse/CareRecords.vue"; +import NurseHealth from "../views/nurse/HealthRecords.vue"; +import NurseHandovers from "../views/nurse/Handovers.vue"; +import NurseNotices from "../views/nurse/Notices.vue"; + +import FamilyDashboard from "../views/family/Dashboard.vue"; +import FamilyElders from "../views/family/Elders.vue"; +import FamilyCare from "../views/family/CareRecords.vue"; +import FamilyHealth from "../views/family/HealthRecords.vue"; +import FamilyBills from "../views/family/Bills.vue"; +import FamilyFeedback from "../views/family/Feedback.vue"; +import FamilyNotices from "../views/family/Notices.vue"; + +Vue.use(Router); + +const router = new Router({ + mode: "history", + routes: [ + { path: "/", redirect: "/login" }, + { path: "/login", component: Login }, + { path: "/register", component: Register }, + { + path: "/admin", + component: Layout, + meta: { role: "ADMIN" }, + children: [ + { path: "", redirect: "dashboard" }, + { path: "dashboard", component: AdminDashboard }, + { path: "users", component: AdminUsers }, + { path: "elders", component: AdminElders }, + { path: "schedules", component: AdminSchedules }, + { path: "bills", component: AdminBills }, + { path: "feedback", component: AdminFeedback }, + { path: "notices", component: AdminNotices } + ] + }, + { + path: "/nurse", + component: Layout, + meta: { role: "NURSE" }, + children: [ + { path: "", redirect: "dashboard" }, + { path: "dashboard", component: NurseDashboard }, + { path: "schedules", component: NurseSchedules }, + { path: "care", component: NurseCare }, + { path: "health", component: NurseHealth }, + { path: "handovers", component: NurseHandovers }, + { path: "notices", component: NurseNotices } + ] + }, + { + path: "/family", + component: Layout, + meta: { role: "FAMILY" }, + children: [ + { path: "", redirect: "dashboard" }, + { path: "dashboard", component: FamilyDashboard }, + { path: "elders", component: FamilyElders }, + { path: "care", component: FamilyCare }, + { path: "health", component: FamilyHealth }, + { path: "bills", component: FamilyBills }, + { path: "feedback", component: FamilyFeedback }, + { path: "notices", component: FamilyNotices } + ] + } + ] +}); + +router.beforeEach((to, from, next) => { + const token = localStorage.getItem("token"); + const role = localStorage.getItem("role"); + if (to.path === "/login" || to.path === "/register") { + return next(); + } + if (!token) { + return next("/login"); + } + if (to.meta && to.meta.role && to.meta.role !== role) { + if (role === "ADMIN") return next("/admin"); + if (role === "NURSE") return next("/nurse"); + if (role === "FAMILY") return next("/family"); + } + return next(); +}); + +export default router; diff --git a/frontend/src/utils/date.js b/frontend/src/utils/date.js new file mode 100644 index 0000000..0d91483 --- /dev/null +++ b/frontend/src/utils/date.js @@ -0,0 +1,20 @@ +export function formatDate(value) { + if (!value) return ""; + const d = new Date(value); + const y = d.getFullYear(); + const m = String(d.getMonth() + 1).padStart(2, "0"); + const day = String(d.getDate()).padStart(2, "0"); + return `${y}-${m}-${day}`; +} + +export function formatDateTime(value) { + if (!value) return ""; + const d = new Date(value); + const y = d.getFullYear(); + const m = String(d.getMonth() + 1).padStart(2, "0"); + const day = String(d.getDate()).padStart(2, "0"); + const hh = String(d.getHours()).padStart(2, "0"); + const mm = String(d.getMinutes()).padStart(2, "0"); + const ss = String(d.getSeconds()).padStart(2, "0"); + return `${y}-${m}-${day}T${hh}:${mm}:${ss}`; +} diff --git a/frontend/src/views/Layout.vue b/frontend/src/views/Layout.vue new file mode 100644 index 0000000..9662b7c --- /dev/null +++ b/frontend/src/views/Layout.vue @@ -0,0 +1,96 @@ + + + diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue new file mode 100644 index 0000000..74e85f0 --- /dev/null +++ b/frontend/src/views/Login.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/frontend/src/views/Register.vue b/frontend/src/views/Register.vue new file mode 100644 index 0000000..c1c2bbc --- /dev/null +++ b/frontend/src/views/Register.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/frontend/src/views/admin/Bills.vue b/frontend/src/views/admin/Bills.vue new file mode 100644 index 0000000..7eac007 --- /dev/null +++ b/frontend/src/views/admin/Bills.vue @@ -0,0 +1,69 @@ + + + diff --git a/frontend/src/views/admin/Dashboard.vue b/frontend/src/views/admin/Dashboard.vue new file mode 100644 index 0000000..4412f1e --- /dev/null +++ b/frontend/src/views/admin/Dashboard.vue @@ -0,0 +1,49 @@ + + + diff --git a/frontend/src/views/admin/Elders.vue b/frontend/src/views/admin/Elders.vue new file mode 100644 index 0000000..263487a --- /dev/null +++ b/frontend/src/views/admin/Elders.vue @@ -0,0 +1,128 @@ + + + diff --git a/frontend/src/views/admin/Feedback.vue b/frontend/src/views/admin/Feedback.vue new file mode 100644 index 0000000..6dfadaa --- /dev/null +++ b/frontend/src/views/admin/Feedback.vue @@ -0,0 +1,76 @@ + + + diff --git a/frontend/src/views/admin/Notices.vue b/frontend/src/views/admin/Notices.vue new file mode 100644 index 0000000..77b1e2e --- /dev/null +++ b/frontend/src/views/admin/Notices.vue @@ -0,0 +1,70 @@ + + + diff --git a/frontend/src/views/admin/Schedules.vue b/frontend/src/views/admin/Schedules.vue new file mode 100644 index 0000000..cd465b8 --- /dev/null +++ b/frontend/src/views/admin/Schedules.vue @@ -0,0 +1,108 @@ + + + diff --git a/frontend/src/views/admin/Users.vue b/frontend/src/views/admin/Users.vue new file mode 100644 index 0000000..acdcc9d --- /dev/null +++ b/frontend/src/views/admin/Users.vue @@ -0,0 +1,132 @@ + + + diff --git a/frontend/src/views/family/Bills.vue b/frontend/src/views/family/Bills.vue new file mode 100644 index 0000000..f247a32 --- /dev/null +++ b/frontend/src/views/family/Bills.vue @@ -0,0 +1,60 @@ + + + diff --git a/frontend/src/views/family/CareRecords.vue b/frontend/src/views/family/CareRecords.vue new file mode 100644 index 0000000..1f4faef --- /dev/null +++ b/frontend/src/views/family/CareRecords.vue @@ -0,0 +1,44 @@ + + + diff --git a/frontend/src/views/family/Dashboard.vue b/frontend/src/views/family/Dashboard.vue new file mode 100644 index 0000000..a545552 --- /dev/null +++ b/frontend/src/views/family/Dashboard.vue @@ -0,0 +1,6 @@ + diff --git a/frontend/src/views/family/Elders.vue b/frontend/src/views/family/Elders.vue new file mode 100644 index 0000000..881ef3d --- /dev/null +++ b/frontend/src/views/family/Elders.vue @@ -0,0 +1,29 @@ + + + diff --git a/frontend/src/views/family/Feedback.vue b/frontend/src/views/family/Feedback.vue new file mode 100644 index 0000000..bffb57c --- /dev/null +++ b/frontend/src/views/family/Feedback.vue @@ -0,0 +1,57 @@ + + + diff --git a/frontend/src/views/family/HealthRecords.vue b/frontend/src/views/family/HealthRecords.vue new file mode 100644 index 0000000..519c477 --- /dev/null +++ b/frontend/src/views/family/HealthRecords.vue @@ -0,0 +1,46 @@ + + + diff --git a/frontend/src/views/family/Notices.vue b/frontend/src/views/family/Notices.vue new file mode 100644 index 0000000..737c8f8 --- /dev/null +++ b/frontend/src/views/family/Notices.vue @@ -0,0 +1,28 @@ + + + diff --git a/frontend/src/views/nurse/CareRecords.vue b/frontend/src/views/nurse/CareRecords.vue new file mode 100644 index 0000000..5f0d60d --- /dev/null +++ b/frontend/src/views/nurse/CareRecords.vue @@ -0,0 +1,72 @@ + + + diff --git a/frontend/src/views/nurse/Dashboard.vue b/frontend/src/views/nurse/Dashboard.vue new file mode 100644 index 0000000..5e12bc1 --- /dev/null +++ b/frontend/src/views/nurse/Dashboard.vue @@ -0,0 +1,6 @@ + diff --git a/frontend/src/views/nurse/Handovers.vue b/frontend/src/views/nurse/Handovers.vue new file mode 100644 index 0000000..1157f6b --- /dev/null +++ b/frontend/src/views/nurse/Handovers.vue @@ -0,0 +1,52 @@ + + + diff --git a/frontend/src/views/nurse/HealthRecords.vue b/frontend/src/views/nurse/HealthRecords.vue new file mode 100644 index 0000000..6e35702 --- /dev/null +++ b/frontend/src/views/nurse/HealthRecords.vue @@ -0,0 +1,67 @@ + + + diff --git a/frontend/src/views/nurse/Notices.vue b/frontend/src/views/nurse/Notices.vue new file mode 100644 index 0000000..bb829a8 --- /dev/null +++ b/frontend/src/views/nurse/Notices.vue @@ -0,0 +1,28 @@ + + + diff --git a/frontend/src/views/nurse/Schedules.vue b/frontend/src/views/nurse/Schedules.vue new file mode 100644 index 0000000..1393d84 --- /dev/null +++ b/frontend/src/views/nurse/Schedules.vue @@ -0,0 +1,36 @@ + + + diff --git a/frontend/vue.config.js b/frontend/vue.config.js new file mode 100644 index 0000000..60205e2 --- /dev/null +++ b/frontend/vue.config.js @@ -0,0 +1,15 @@ +module.exports = { + devServer: { + port: 5173, + proxy: { + "/api": { + target: "http://localhost:8080", + changeOrigin: true + }, + "/files": { + target: "http://localhost:8080", + changeOrigin: true + } + } + } +}; diff --git a/张宇-开题报告.md b/张宇-开题报告.md new file mode 100644 index 0000000..ac6f4ff --- /dev/null +++ b/张宇-开题报告.md @@ -0,0 +1,253 @@ +# 大连科技学院 + +# 毕业设计(论文)开题报告 + +学院 信息科学与技术学院 + +专业班级 网络工程(专升本)24-1 + +学生姓名 张宇 + +学生学号 2406490115 + +指导教师 张殿桢 + +导师职称 工程师 + +# 1 选题的意义和研究现状 + +# 1.1 选题的意义 + +当前,全球性的人口老龄化浪潮已成为一个不容忽视的议题,我国在这一趋势下面临的形势尤为突出。根据官方发布的最新统计,截至2023年末,我国60岁及以上的人口规模已达2.97亿,占据了总人口的 $21.1\%$ ;与此同时,65岁及以上的老年群体也已增至2.17亿,占比达到 $15.4\%$ 。这一系列数据直观地表明,我国社会正承受着日益沉重的人口结构压力,如何应对老龄化已成为一项必须正视的挑战。随着社会加速迈向深度老龄化阶段,养老难题如何破解,便顺理成章地成为各界关注的焦点。基于此,探索并运用智能化养老方案来应对这一现实需求,就显得格外迫切。 + +本研究聚焦养老院管理平台的设计与实现,力求借助现代信息技术手段,为养老机构的管理效率与服务水准带来根本性的改进。传统的养老院管理模式,严重依赖纸质台账和人工协调,在效率、精准度和透明度上,已经远远无法跟上现代养老服务的需求。 + +本课题为了解决这一系列管理难题,比如流程繁琐、信息孤岛现象严重、数据统计滞后,以及服务响应不够迅速等,核心目标便是开发一套综合性的信息系统。该系统旨在将 + +养老院运营中的“人、财、物、服务”这四大核心要素进行全面的数字化整合,最终能够助力养老机构实现“降本、提质、增效”的数字化转型,从而彻底打破传统管理模式下的信息壁垒,迈向业务在线化、管理可视化和决策数据化的新阶段。 + +本课题的技术价值在于,通过基于前后端分离架构的智慧养老系统设计与实现,为同类平台的开发提供了具有参考价值的实践方案与架构范例。在实践层面,“颐云养老院管理平台”的实施将有效优化养老机构的内部管理流程,提升工作人员协同效率与服务响应速度。同时,系统通过构建面向家属的客户端功能,增强了服务过程的透明度与沟通效率,有助于提升家属信任度与服务满意度。整体上,本系统为应对人口老龄化背景下养老服务资源优化与服务模式创新,提供了可行的技术路径与系统化支持。 + +# 1.2本课题所涉及问题在国内外设计或研究的现状 + +# 1.2.1 国内外研究现状 + +在国内,相关研究的焦点普遍集中在如何推动养老服务走向信息化与智能化。无论是学术界还是产业界,都有大量企业投身于各类养老管理信息系统的开发之中。这些系统的功能设计通常相当全面,从老人档案管理、日常健康监测,到护理计划制定乃至费用结算等,其核心目的都是为了提升养老机构的运营效率。举个例子,一些前沿研究已经开 + +始引入物联网(IoT)技术,对老人的生命体征和活动状态进行全天候的实时监控,从而能够做到主动预警和快速响应。与此同时,另一个值得注意的趋势是基于大数据分析的应用正悄然兴起,通过对老人健康数据和行为模式的深度挖掘,系统能够为他们量身定制个性化的护理与康复建议。 + +在国外,他们在养老科技领域的探索不仅起步更早,体系也已经发展得更为成熟。可以说,国际上的研究视野早已不局限于单纯的技术功能实现,一个很明显的趋势是,他们更加注重服务的个性化、人文关怀与智能化水平。许多先进的养老系统设计理念,都倾向于整合更为全面的服务维度,将重心从满足基本的生理照护,进一步拓展到关怀老年人的心理健康与社会交往需求。同时,在人工智能(AI)、物联网等前沿技术的应用上,其研究的触角也伸得更深,覆盖了从无感化环境监测到智能交互式陪护等多个层面,其最终目的都是为了给老年人营造一个更安全、便捷且富有人文关怀的居住环境。此外,国外研究还有一个普遍的特点,那就是极其强调用户体验(UX),尤其是在人机交互设计方面,会充分考虑到老年群体的特殊性,以确保技术足够普惠和易用。 + +综合来看,无论国内还是国外,关于养老院管理平台的研究方向可以说正在趋于一致,核心目标都是希望借助信息化和智能化的手段,来提升管理效率与服务质量。区别在 + +于,国内的研究更侧重于快速跟进和应用最新的技术,而国外则在服务的深度、广度以及 + +人文关怀这些方面,积累了更为丰富的经验。“颐云养老院管理平台”的设计,恰好顺应 + +了这一发展趋势。它的目标很明确,就是希望能结合国内外先进的经验,并利用当下成熟 + +稳定的 SpringBoot 与 Vue 技术栈,为我国构建一个既符合自身国情、又具备全面功能且 + +易于操作的养老院管理解决方案。综合来看,无论国内还是国外,关于养老院管理平台的 + +研究方向可以说正在趋于一致,核心目标都是希望借助信息化和智能化的手段,来提升 + +管理效率与服务质量。区别在于,国内的研究更侧重于快速跟进和应用最新的技术,而国 + +外则在服务的深度、广度以及人文关怀这些方面,积累了更为丰富的经验。“颐云养老院 + +管理平台”的设计,恰好顺应了这一发展趋势。它的目标很明确,就是希望能结合国内外 + +先进的经验,并利用当下成熟稳定的 SpringBoot 与 Vue 技术栈,为社会构建一个既符合 + +自身国情、又具备全面功能且易于操作的养老院管理解决方案。 + +# 2 课题设计或研究的内容、预期目标和实施计划 + +# 2.1 要设计或研究的主要内容方案论证分析 + +# 2.1.1 主要研究内容 + +本课题旨在设计并实现一个名为“颐云养老院管理平台”的综合性信息系统。系统采用B/S架构,基于SpringBoot + Vue + MySql + ElementUI技术栈进行开发。平台主要服务于管理员、护工和家属三类用户,通过为不同角色提供精准的功能模块,实现对养老院业务流程的全面数字化管理。具体功能模块划分如下: + +# (1) 护工用户功能 + +登录:护工通过唯一的账号和密码登录系统,访问其权限范围内的功能。 + +我的排班:查看个人的工作班次、时间及具体任务安排,便于护工提前规划工作。 + +护理记录:记录对长者的日常护理内容,如喂食、翻身、清洁等,并形成可追溯的服务档案。 + +健康监测:为所负责的长者录入生命体征数据(如体温、血压、心率),并记录异常情况。 + +交班报告:在轮班交接时,填写交接班记录,说明重点关注的长者情况和未完成事项。 + +通知中心:查看由管理员发布的内部通知、培训安排等重要信息。 + +# (2) 家属用户功能 + +注册:家属在系统中注册账号,并输入长者的身份证号进行关系绑定。 + +登录:家属通过唯一的账号和密码登录系统,访问其权限范围内的功能。 + +亲人档案:查看亲属在养老院的基本档案、入住信息和护理等级。 + +每日动态:实时查阅由护工录入的亲属每日健康数据和护理记录,远程了解老人健康状况。 + +账单与支付:查询月度账单(如床位费、护理费、餐饮费等),并支持在线完成支付。 + +服务反馈:对养老院的服务提出意见、建议或投诉,并可以对护工的服务进行评价。 + +# (3) 管理员用户功能模块 + +运营管理:总览平台核心数据:在住长者数、员工数、收入情况等统计信息。 + +账户管理:管理员可以对员工以及家属的账号进行信息修改,禁用等操作。 + +财务管理:设置收费项目标准,生成每位老人的月度账单,并管理缴费记录。 + +沟通与改进:查看并处理来自家属的投诉与评价,及时做出响应和改进,并对处理结果进行记录。 + +排班管理:为员工安排工作班次,支持在线调整与发布。 + +档案管理:对长者的档案信息进行管理,确保数据完整且准确无误。 + +总体功能图如图1所示。 + +![image](https://cdn-mineru.openxlab.org.cn/result/2026-01-20/fd22b613-e4fb-4b10-b44e-5a02e882b31c/87ec6d47c69aee2ac7e072d6e001ec59d6e2f3653398b99c81c5a946623b5b75.jpg) + + + +图1总体功能图 + + +# 2.1.2 研究方案及可行性分析 + +本研究的核心方案在于设计并实现一个采用前后端分离架构的Web应用。在前端层面,我们将利用Vue框架与ElementUI组件库相结合,致力于构建一个直观、友好的用户交互界面。而在后端,则选用Java语言及主流的SpringBoot框架,集中处理系统的核心业务逻辑、完成数据持久化,并开发相应的API接口。数据库方面,我们选择了开源且性能稳定的MySQL来承担数据存储任务。在此基础上,前后端将通过RESTful API规范进行数据通信,以此确保二者之间的高效协同与信息交换。 + +本研究的实施将严格遵循标准的软件工程规范,以确保项目的科学性与高质量。我们将首先启动详尽的需求分析阶段,通过深入的实地调研和多方沟通,旨在精准捕获并明确定义各角色的功能需求、业务流程及非功能性需求,从而为系统构建清晰、无歧义的目标蓝图。需求明确后,工作将平滑过渡到系统设计阶段,该环节将涵盖软件架构的设 + +计、数据库结构的设计与优化(以E-R图为核心工具),形成指导开发工作的技术蓝图。 + +为最终保障系统的稳定与可靠,项目后期将执行严谨的系统测试策略,覆盖单元、集成和 + +功能等多个层面,确保从代码底层到业务顶层的层层质量保障。对于测试中发现的任何 + +缺陷,都将建立闭环管理机制,进行彻底的修复与回归验证,确保最终交付的系统不仅在 + +功能上满足预期,更在质量上达到工业级应用标准。 + +# 2.1.3 可行性分析 + +# (1) 技术可行性 + +本系统拟采用的技术均为当前业界主流且成熟的技术。Java语言拥有庞大的开发者 + +社区和丰富的生态系统。SpringBoot 框架极大地简化了 Spring 应用的搭建和开发过程, + +能快速构建稳定可靠的后端服务。Vue 作为渐进式 JavaScript 框架, 配合 ElementUI, 能够 + +高效地开发出现代化、响应式的用户界面。MySQL是应用最广泛的关系型数据库之一, + +这些技术均有完善的官方文档和丰富的开源资源,开发难度可控,因此技术上完全可行。 + +# (2) 经济可行性 + +本系统在开发过程中主要采用了一系列开源及免费工具,包括 Java 开发环境(JDK)、SpringBoot 后端框架、Vue 前端框架、MySQL 关系型数据库以及 Visual Studio + +Code 编辑器等,有效避免了昂贵的商业软件授权支出。而在部署层面,所涉及的服务器 + +硬件与网络带宽资源,对于大多数中小型养老机构而言,在现有预算内亦具备可操作性。 + +从生命周期成本与回报的角度综合评估,该项目具备较高的投入产出比。因此,本课题在 + +经济上是可行的。 + +# (3) 社会可行性 + +随着我国“积极应对人口老龄化”国家战略的推进和“智慧养老”概念的普及,利 + +用信息技术提升养老服务质量已成为社会共识。本平台的设计紧密贴合养老院的实际运 + +营需求,旨在解决管理痛点,提升服务水平,符合社会发展的趋势。因此,该项目具有显 + +著的社会价值和广泛的应用前景,社会可行性高。 + +# 2.2本课题选题特色及预期的目标 + +本课题的特色在于其高度的针对性和实用性。为管理员、护工、家属三个关键角色量 + +身定制功能。采用成熟的 SpringBoot + Vue 前后端分离技术架构,确保了系统的高性能、 + +高可维护性和良好的可扩展性。项目致力于打破传统养老院管理中的“信息孤岛”,实 + +现业务流程的在线化、管理数据的可视化和运营决策的数据化支持。 + +预期目标是通过系统的开发与应用,实现养老院日常管理的数字化和智能化,优化 + +服务流程,提升老年人照护质量和家属的满意度,推动养老服务的现代化发展。 + +# 2.3 本课题实施计划 + +
周数进度计划
第1周确定毕业设计题目,在网络上对“养老院管理平台”进行调研
第2周根据前期的调研情况,查阅相关资料完成开题报告撰写
第3周选择与课题相关的外文文献,完成外文翻译。进行前期资料自查,进行系统可行性分析和需求分析
第4周完成毕设前期检查。依据系统功能需求和业务流程分析,完成用例图和用例说明
第5周进行系统分析,以用例图为基础进行类图、活动图和顺序图的绘制,确保系统的一致性和可维护性
第6周完成数据库设计、界面设计,根据反馈意见进行修改
第7周系统实现,按功能模块进行编码
第8周完成毕设中期检查。系统实现,按功能模块进行编码
第9周系统测试,测试本系统各业务功能运行是否正常,验证功能需求是否都符合规范要求。完成论文主体部分
第10周按照系统测试结果修改代码完善功能,并完成论文剩余相关内容编写
第11周提交论文初稿,根据反馈意见修改论文
第12周继续修改论文,完成论文查重稿定稿。检查系统功能,为软件验收做好准备
第13周进行软件验收,参加校级论文查重,根据论文内容制作答辩PPT
第14周进行毕业设计答辩,并按照答辩组意见修改论文定稿,完成毕设资料存档
+ +# 3 主要参考文献 + + + +[1] 吴秋燕, 赵杰, 邹慧淋等. 智能家庭养老系统的设计与实现[J], 物联网技术, 2023, 13(09): 111-113. + + + + + +[2] 张琴.养老院智能管理系统的设计与实现[J],信息与电脑,2022,34(13):158-160. + + + + + +[3], [4] 魏娇, 白磊. 基于智能数字化的智慧养老管理系统设计[J], 鞋类工艺与设计, 2022, 2(24):168-170. + + + + + +[5] 王曼维, 杨荻, 李岩等. 基于 SpringBoot 框架的智慧医疗问诊系统设计与实现[J], 中国医学装备, 2022, 19(03): 133-136. + + + + + +[6] 单树倩,任佳勋.基于 SpringBoot 和 Vue 框架的数据库原理网站设计与实现[J],电脑知识与技术:学术版,2021,17(30):40-41. + + + + + +[7] Evaluating the impact of retirement leisure planning on retirement satisfaction and vitality: a mixed-method study[J]. ANNALS OF LEISURE RESEARCH, 2023, 26(01): 140-161. + + + + + +[8] 陈菲.老年健康服务产业高质量发展战略路径研究[M],重庆大学出版社:202302.164. + + + + + +[9] 马岚.公益性和产业化相结合的养老服务模式研究[M],南京大学出版社:202107.196. + + + + + +[10] 邹平吉. JavaWeb 文化遗产数字化信息管理平台[J], 数字技术与应用, 2025(1):202-204. + + + + + +[11] 郭佳丽.H养老院医养信息系统的设计与实现[D],首都经济贸易大学,2022. +