用药管理功能旨在为患者提供一个便捷的用药记录和管理平台。该功能允许患者添加、编辑、删除和查看自己的用药信息,包括药品名称、剂量、服用频率、服用时间等。医生也可以查看患者的用药记录,以便更好地了解患者的治疗情况。
根据项目数据库表设计规范(参见 docs/OLD/DevRule/07-数据库规范.md),创建患者用药记录表:
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | BIGINT(20) | 主键ID,使用雪花算法(MyBatis-Plus ASSIGN_ID) |
| patient_user_id | BIGINT(20) | 患者用户ID,外键关联用户表 |
| medicine_name | VARCHAR(100) | 药品名称 |
| dosage | VARCHAR(50) | 剂量规格(如:100mg、0.5g) |
| frequency | VARCHAR(100) | 服用频率(如:每日1次、每日3次每次1粒) |
| times | TEXT | 服用时间点列表(JSON格式存储) |
| note | TEXT | 备注信息 |
| version | INT(11) | 版本号(乐观锁) |
| create_user | BIGINT(20) | 创建者ID |
| create_time | DATETIME | 创建时间,默认值CURRENT_TIMESTAMP |
| update_user | BIGINT(20) | 更新者ID |
| update_time | DATETIME | 更新时间,默认值CURRENT_TIMESTAMP,更新时自动设置为当前时间 |
| remark | VARCHAR(255) | 备注 |
遵循的数据库规范要点:
BIGINT 主键并通过雪花算法生成(MyBatis-Plus ASSIGN_ID)。version、create_user、create_time、update_user、update_time)由 BaseEntity 与 CustomMetaObjectHandler 自动填充。V{version}__{description}.sql)。示例建表(参考):
CREATE TABLE `t_patient_medication` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`patient_user_id` bigint(20) NOT NULL COMMENT '患者用户ID',
`medicine_name` varchar(100) NOT NULL COMMENT '药品名称',
`dosage` varchar(50) NOT NULL COMMENT '剂量规格',
`frequency` varchar(100) NOT NULL COMMENT '服用频率',
`times` text COMMENT '服用时间点列表(JSON格式存储)',
`note` text COMMENT '备注信息',
`version` int(11) DEFAULT '0' COMMENT '版本号(乐观锁)',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建者ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_user` bigint(20) DEFAULT NULL COMMENT '更新者ID',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `idx_patient_user_id` (`patient_user_id`),
KEY `idx_medicine_name` (`medicine_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='患者用药记录表';
备注:对外响应的 id 建议以字符串形式返回以避免前端 JS 精度丢失(参见 docs/OLD/DevRule/03-API设计规范.md 的长整型 ID 传输策略)。
package work.baiyun.chronicdiseaseapp.model.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@Schema(description = "患者用药记录表")
@TableName("t_patient_medication")
@Data
@EqualsAndHashCode(callSuper = false)
public class PatientMedication extends BaseEntity {
@Schema(description = "患者用户ID")
@TableField("patient_user_id")
private Long patientUserId;
@Schema(description = "药品名称")
@TableField("medicine_name")
private String medicineName;
@Schema(description = "剂量规格")
@TableField("dosage")
private String dosage;
@Schema(description = "服用频率")
@TableField("frequency")
private String frequency;
@Schema(description = "服用时间点列表(JSON格式存储)")
@TableField("times")
private String times;
@Schema(description = "备注信息")
@TableField("note")
private String note;
}
// CreateMedicationRequest.java
package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.List;
@Schema(description = "创建用药记录请求")
@Data
public class CreateMedicationRequest {
@Schema(description = "患者用户ID", required = true)
@NotNull(message = "患者用户ID不能为空")
private Long patientUserId;
@Schema(description = "药品名称", required = true)
@NotBlank(message = "药品名称不能为空")
@Size(max = 100, message = "药品名称长度不能超过100个字符")
private String medicineName;
@Schema(description = "剂量规格", required = true)
@NotBlank(message = "剂量规格不能为空")
@Size(max = 50, message = "剂量规格长度不能超过50个字符")
private String dosage;
@Schema(description = "服用频率", required = true)
@NotBlank(message = "服用频率不能为空")
@Size(max = 100, message = "服用频率长度不能超过100个字符")
private String frequency;
@Schema(description = "服用时间点列表", required = true)
@NotNull(message = "服用时间点列表不能为空")
private List<String> times;
@Schema(description = "备注信息")
private String note;
}
// UpdateMedicationRequest.java
package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.List;
@Schema(description = "更新用药记录请求")
@Data
public class UpdateMedicationRequest {
@Schema(description = "用药记录ID", required = true)
@NotNull(message = "用药记录ID不能为空")
private Long id;
@Schema(description = "药品名称", required = true)
@NotBlank(message = "药品名称不能为空")
@Size(max = 100, message = "药品名称长度不能超过100个字符")
private String medicineName;
@Schema(description = "剂量规格", required = true)
@NotBlank(message = "剂量规格不能为空")
@Size(max = 50, message = "剂量规格长度不能超过50个字符")
private String dosage;
@Schema(description = "服用频率", required = true)
@NotBlank(message = "服用频率不能为空")
@Size(max = 100, message = "服用频率长度不能超过100个字符")
private String frequency;
@Schema(description = "服用时间点列表", required = true)
@NotNull(message = "服用时间点列表不能为空")
private List<String> times;
@Schema(description = "备注信息")
private String note;
}
// MedicationQueryRequest.java
package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "用药记录查询请求")
@Data
@EqualsAndHashCode(callSuper = true)
public class MedicationQueryRequest extends BaseQueryRequest {
@Schema(description = "患者用户ID")
private Long patientUserId;
@Schema(description = "药品名称关键字")
private String medicineNameKeyword;
}
// MedicationResponse.java
package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用药记录响应")
@Data
public class MedicationResponse {
@Schema(description = "用药记录ID")
private String id;
@Schema(description = "患者用户ID")
private Long patientUserId;
@Schema(description = "药品名称")
private String medicineName;
@Schema(description = "剂量规格")
private String dosage;
@Schema(description = "服用频率")
private String frequency;
@Schema(description = "服用时间点列表")
private List<String> times;
@Schema(description = "备注信息")
private String note;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}
// MedicationPageResponse.java
package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "用药记录分页响应")
@Data
public class MedicationPageResponse {
@Schema(description = "数据列表")
private List<MedicationResponse> records;
@Schema(description = "总数")
private long total;
@Schema(description = "每页大小")
private long size;
@Schema(description = "当前页码")
private long current;
@Schema(description = "总页数")
private long pages;
}
// PatientMedicationMapper.java
package work.baiyun.chronicdiseaseapp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import work.baiyun.chronicdiseaseapp.model.po.PatientMedication;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PatientMedicationMapper extends BaseMapper<PatientMedication> {
}
// PatientMedicationService.java
package work.baiyun.chronicdiseaseapp.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationQueryRequest;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationResponse;
import work.baiyun.chronicdiseaseapp.model.vo.CreateMedicationRequest;
import work.baiyun.chronicdiseaseapp.model.vo.UpdateMedicationRequest;
public interface PatientMedicationService {
/**
* 创建用药记录
*/
void createMedication(CreateMedicationRequest request);
/**
* 更新用药记录
*/
void updateMedication(UpdateMedicationRequest request);
/**
* 分页查询用药记录
*/
Page<MedicationResponse> listMedications(MedicationQueryRequest request);
/**
* 根据ID删除用药记录
*/
void deleteMedication(Long id);
/**
* 根据ID获取患者用药记录详情
*/
MedicationResponse getMedicationById(Long id);
/**
* 根据患者ID获取患者用药记录列表
*/
List<MedicationResponse> listMedicationsByPatientId(Long patientUserId);
}
// PatientMedicationServiceImpl.java
package work.baiyun.chronicdiseaseapp.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import work.baiyun.chronicdiseaseapp.mapper.PatientMedicationMapper;
import work.baiyun.chronicdiseaseapp.model.po.PatientMedication;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationQueryRequest;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationResponse;
import work.baiyun.chronicdiseaseapp.model.vo.CreateMedicationRequest;
import work.baiyun.chronicdiseaseapp.model.vo.UpdateMedicationRequest;
import work.baiyun.chronicdiseaseapp.service.PatientMedicationService;
import work.baiyun.chronicdiseaseapp.util.JsonUtils;
import work.baiyun.chronicdiseaseapp.util.SecurityUtils;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class PatientMedicationServiceImpl implements PatientMedicationService {
@Autowired
private PatientMedicationMapper medicationMapper;
@Override
public void createMedication(CreateMedicationRequest request) {
PatientMedication medication = new PatientMedication();
BeanUtils.copyProperties(request, medication);
medication.setTimes(JsonUtils.toJson(request.getTimes()));
medication.setPatientUserId(request.getPatientUserId());
medicationMapper.insert(medication);
}
@Override
public void updateMedication(UpdateMedicationRequest request) {
PatientMedication medication = medicationMapper.selectById(request.getId());
if (medication == null) {
throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
"用药记录不存在");
}
BeanUtils.copyProperties(request, medication);
medication.setTimes(JsonUtils.toJson(request.getTimes()));
medicationMapper.updateById(medication);
}
@Override
public Page<MedicationResponse> listMedications(MedicationQueryRequest request) {
Page<PatientMedication> page = new Page<>(request.getPageNum(), request.getPageSize());
LambdaQueryWrapper<PatientMedication> wrapper = new LambdaQueryWrapper<>();
// 根据患者ID筛选
wrapper.eq(request.getPatientUserId() != null, PatientMedication::getPatientUserId, request.getPatientUserId());
// 根据药品名称关键字搜索
if (request.getMedicineNameKeyword() != null && !request.getMedicineNameKeyword().trim().isEmpty()) {
wrapper.like(PatientMedication::getMedicineName, request.getMedicineNameKeyword().trim());
}
// 时间范围筛选
wrapper.ge(request.getStartTime() != null, PatientMedication::getCreateTime, request.getStartTime())
.le(request.getEndTime() != null, PatientMedication::getCreateTime, request.getEndTime())
.orderByDesc(PatientMedication::getCreateTime);
Page<PatientMedication> result = medicationMapper.selectPage(page, wrapper);
List<MedicationResponse> responses = result.getRecords().stream().map(this::convertToResponse).collect(Collectors.toList());
Page<MedicationResponse> responsePage = new Page<>();
responsePage.setRecords(responses);
responsePage.setCurrent(result.getCurrent());
responsePage.setSize(result.getSize());
responsePage.setTotal(result.getTotal());
responsePage.setPages(result.getPages());
return responsePage;
}
@Override
public void deleteMedication(Long id) {
PatientMedication medication = medicationMapper.selectById(id);
if (medication == null) {
throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
"用药记录不存在");
}
medicationMapper.deleteById(id);
}
@Override
public MedicationResponse getMedicationById(Long id) {
PatientMedication medication = medicationMapper.selectById(id);
if (medication == null) {
throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
"用药记录不存在");
}
return convertToResponse(medication);
}
@Override
public List<MedicationResponse> listMedicationsByPatientId(Long patientUserId) {
LambdaQueryWrapper<PatientMedication> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PatientMedication::getPatientUserId, patientUserId)
.orderByDesc(PatientMedication::getCreateTime);
List<PatientMedication> medications = medicationMapper.selectList(wrapper);
return medications.stream().map(this::convertToResponse).collect(Collectors.toList());
}
private MedicationResponse convertToResponse(PatientMedication medication) {
MedicationResponse response = new MedicationResponse();
BeanUtils.copyProperties(medication, response);
response.setId(medication.getId().toString());
response.setTimes(JsonUtils.fromJson(medication.getTimes(), new TypeReference<List<String>>(){}));
return response;
}
}
// PatientMedicationController.java
package work.baiyun.chronicdiseaseapp.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import work.baiyun.chronicdiseaseapp.common.R;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationQueryRequest;
import work.baiyun.chronicdiseaseapp.model.vo.MedicationResponse;
import work.baiyun.chronicdiseaseapp.model.vo.CreateMedicationRequest;
import work.baiyun.chronicdiseaseapp.model.vo.UpdateMedicationRequest;
import work.baiyun.chronicdiseaseapp.service.MedicationService;
import work.baiyun.chronicdiseaseapp.enums.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
@RestController
@RequestMapping("/patient-medication")
@Tag(name = "用药管理", description = "用药管理相关接口")
public class PatientMedicationController {
private static final Logger logger = LoggerFactory.getLogger(MedicationController.class);
@Autowired
private MedicationService medicationService;
@Operation(summary = "创建用药记录", description = "创建新的用药记录")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "用药记录创建成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@PostMapping(path = "/create", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> create(@RequestBody CreateMedicationRequest req) {
try {
medicationService.createMedication(req);
return R.success(200, "用药记录创建成功");
} catch (Exception e) {
logger.error("create medication failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "更新用药记录", description = "更新用药记录")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "用药记录更新成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@PutMapping(path = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> update(@PathVariable Long id, @RequestBody UpdateMedicationRequest req) {
try {
// 将路径ID赋值到请求对象,保证一致性
req.setId(id);
medicationService.updateMedication(req);
return R.success(200, "用药记录更新成功");
} catch (Exception e) {
logger.error("update medication failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "分页查询用药记录", description = "根据条件和分页参数查询用药记录")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功查询用药记录列表",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.MedicationPageResponse.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@PostMapping(path = "/list", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> list(@RequestBody MedicationQueryRequest req) {
try {
Page<MedicationResponse> page = medicationService.listMedications(req);
work.baiyun.chronicdiseaseapp.model.vo.MedicationPageResponse vo = new work.baiyun.chronicdiseaseapp.model.vo.MedicationPageResponse();
vo.setRecords(page.getRecords());
vo.setTotal(page.getTotal());
vo.setSize(page.getSize());
vo.setCurrent(page.getCurrent());
vo.setPages(page.getPages());
return R.success(200, "ok", vo);
} catch (Exception e) {
logger.error("list medications failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "删除用药记录", description = "根据ID删除用药记录")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "用药记录删除成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@DeleteMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> delete(@PathVariable Long id) {
try {
medicationService.deleteMedication(id);
return R.success(200, "用药记录删除成功");
} catch (Exception e) {
logger.error("delete medication failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "获取用药记录详情", description = "根据ID获取用药记录详细信息")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用药记录信息",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.MedicationResponse.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> get(@PathVariable Long id) {
try {
MedicationResponse medication = medicationService.getMedicationById(id);
return R.success(200, "ok", medication);
} catch (Exception e) {
logger.error("get medication failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "根据患者ID获取用药记录列表", description = "根据患者ID获取用药记录列表")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用药记录列表",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.MedicationResponse.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@GetMapping(path = "/patient/{patientUserId}", produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> listByPatientId(@PathVariable Long patientUserId) {
try {
List<MedicationResponse> medications = medicationService.listMedicationsByPatientId(patientUserId);
return R.success(200, "ok", medications);
} catch (Exception e) {
logger.error("list medications by patient id failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
}
// JsonUtils.java
package work.baiyun.chronicdiseaseapp.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JsonUtils {
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("Failed to serialize object to JSON", e);
return null;
}
}
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (JsonProcessingException e) {
logger.error("Failed to deserialize JSON to object", e);
return null;
}
}
public static <T> T fromJson(String json, TypeReference<T> typeReference) {
try {
return objectMapper.readValue(json, typeReference);
} catch (JsonProcessingException e) {
logger.error("Failed to deserialize JSON to object", e);
return null;
}
}
}
在 ErrorCode 枚举中可能需要添加以下错误码:
// 在 ErrorCode.java 中添加
MEDICATION_NOT_FOUND(8000, "用药记录不存在");
POST /patient-medication/create
Content-Type: application/json
{
"patientUserId": 123456,
"medicineName": "阿司匹林",
"dosage": "100mg",
"frequency": "每日1次,每次1粒",
"times": ["08:00"],
"note": "饭后服用"
}
PUT /patient-medication/1
Content-Type: application/json
{
"id": 1,
"medicineName": "阿司匹林肠溶片",
"dosage": "100mg",
"frequency": "每日1次,每次1粒",
"times": ["08:00"],
"note": "饭后服用"
}
POST /patient-medication/list
Content-Type: application/json
{
"pageNum": 1,
"pageSize": 10,
"patientUserId": 123456
}
DELETE /patient-medication/1
GET /patient-medication/1
GET /patient-medication/patient/123456
患者:
医生:
系统管理员:
本项目安全实现应遵循 docs/OLD/DevRule/08-安全规范.md 中的已有约定,以下为与用药管理功能相关的要点(需在实现时落地):
认证与授权:
Authorization: Bearer <token>,兼容 X-Token / token)并通过 AuthInterceptor 校验,将 currentUserId/currentUserRole 注入请求上下文。输入校验:
@NotBlank、@Size 等)对请求参数进行校验。异常与统一错误响应:
@RestControllerAdvice/CustomExceptionHandler)统一映射 CustomException、校验异常、未知异常到 R.fail(code, message),并返回合适的 HTTP 状态码(如 400/403/404/500)。MEDICATION_NOT_FOUND)应使用明确错误码并在 ErrorCode 中定义。日志与敏感信息:
SQL 注入与 ORM 使用:
会话安全与 Token 管理:
t_user_token,有效期 72 小时,低于阈值自动延长)。部署/传输:
classpath:cert/ 并在启动时加载。最小权限原则:
审计与监控: