This commit is contained in:
王子琦
2026-01-20 14:05:18 +08:00
parent 438eb0b635
commit ec6ec210d2
15 changed files with 132 additions and 28 deletions

View File

@@ -14,11 +14,11 @@ public class ApiResponse<T> {
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(0, "ok", data);
return new ApiResponse<>(0, "成功", data);
}
public static <T> ApiResponse<T> success() {
return new ApiResponse<>(0, "ok", null);
return new ApiResponse<>(0, "成功", null);
}
public static <T> ApiResponse<T> error(String message) {

View File

@@ -14,7 +14,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<Void> handleValid(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getAllErrors().isEmpty()
? "validation error" : ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
? "参数校验失败" : ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return ApiResponse.error(msg);
}

View File

@@ -31,13 +31,13 @@ public class AuthController {
public ApiResponse<Map<String, Object>> login(@Valid @RequestBody LoginRequest request) {
User user = userService.findByUsername(request.getUsername());
if (user == null) {
throw new ApiException("user not found");
throw new ApiException("账号不存在");
}
if (user.getStatus() != null && user.getStatus() == 0) {
throw new ApiException("user disabled");
throw new ApiException("账号已被禁用");
}
if (!PasswordUtil.matches(request.getPassword(), user.getPassword())) {
throw new ApiException("invalid password");
throw new ApiException("密码错误");
}
if (!PasswordUtil.isBcrypt(user.getPassword())) {
userService.updatePassword(user.getId(), PasswordUtil.hash(request.getPassword()));

View File

@@ -73,7 +73,7 @@ public class FamilyController {
}
}
if (!allowed) {
throw new ApiException("no access to this elder");
throw new ApiException("无权访问该亲属");
}
return ApiResponse.success(careRecordMapper.listByElderId(elderId));
}
@@ -89,7 +89,7 @@ public class FamilyController {
}
}
if (!allowed) {
throw new ApiException("no access to this elder");
throw new ApiException("无权访问该亲属");
}
return ApiResponse.success(healthRecordMapper.listByElderId(elderId));
}
@@ -105,7 +105,7 @@ public class FamilyController {
}
}
if (!allowed) {
throw new ApiException("no access to this elder");
throw new ApiException("无权访问该亲属");
}
return ApiResponse.success(billService.listByElderId(elderId));
}
@@ -115,7 +115,7 @@ public class FamilyController {
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
Bill bill = billMapper.findById(id);
if (bill == null) {
throw new ApiException("bill not found");
throw new ApiException("账单不存在");
}
boolean allowed = false;
for (FamilyElder relation : familyService.listRelations(familyId)) {
@@ -125,10 +125,10 @@ public class FamilyController {
}
}
if (!allowed) {
throw new ApiException("no access to this bill");
throw new ApiException("无权访问该账单");
}
if ("PAID".equals(bill.getStatus())) {
throw new ApiException("bill already paid");
throw new ApiException("账单已支付");
}
BigDecimal amount = bill.getTotal() == null ? BigDecimal.ZERO : bill.getTotal();
billService.payBill(id, familyId, request.getMethod(), amount);

View File

@@ -21,7 +21,7 @@ public class FileController {
@PostMapping("/upload")
public ApiResponse<Map<String, Object>> upload(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return ApiResponse.error("empty file");
return ApiResponse.error("文件为空");
}
String original = file.getOriginalFilename();
String ext = StringUtils.getFilenameExtension(original);

View File

@@ -3,9 +3,9 @@ package com.nursinghome.dto;
import jakarta.validation.constraints.NotBlank;
public class LoginRequest {
@NotBlank(message = "username required")
@NotBlank(message = "账号不能为空")
private String username;
@NotBlank(message = "password required")
@NotBlank(message = "密码不能为空")
private String password;
public String getUsername() {

View File

@@ -3,14 +3,14 @@ package com.nursinghome.dto;
import jakarta.validation.constraints.NotBlank;
public class RegisterRequest {
@NotBlank(message = "username required")
@NotBlank(message = "账号不能为空")
private String username;
@NotBlank(message = "password required")
@NotBlank(message = "密码不能为空")
private String password;
@NotBlank(message = "name required")
@NotBlank(message = "姓名不能为空")
private String name;
private String phone;
@NotBlank(message = "elderIdCard required")
@NotBlank(message = "亲属身份证号不能为空")
private String elderIdCard;
private String relationship;

View File

@@ -29,7 +29,7 @@ public class ElderService {
public Elder create(Elder elder) {
if (elderMapper.findByIdCard(elder.getIdCard()) != null) {
throw new ApiException("id card already exists");
throw new ApiException("身份证号已存在");
}
elderMapper.insert(elder);
return elder;

View File

@@ -25,7 +25,7 @@ public class FamilyService {
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");
throw new ApiException("未找到该亲属");
}
User user = new User();
user.setUsername(username);

View File

@@ -30,7 +30,7 @@ public class UserService {
public User createUser(User user, boolean hashPassword) {
if (userMapper.findByUsername(user.getUsername()) != null) {
throw new ApiException("username already exists");
throw new ApiException("账号已存在");
}
if (hashPassword) {
user.setPassword(PasswordUtil.hash(user.getPassword()));

View File

@@ -11,7 +11,13 @@
<el-table-column prop="elderId" label="长者ID" width="100" />
<el-table-column prop="month" label="月份" />
<el-table-column prop="total" label="金额" />
<el-table-column prop="status" label="状态" />
<el-table-column label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="statusTag(scope.row.status)">
{{ statusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
<el-dialog title="新建账单" :visible.sync="showCreate">
@@ -47,6 +53,16 @@ export default {
this.load();
},
methods: {
statusLabel(status) {
if (status === "PAID") return "已支付";
if (status === "UNPAID") return "未支付";
return status || "-";
},
statusTag(status) {
if (status === "PAID") return "success";
if (status === "UNPAID") return "warning";
return "info";
},
async load() {
try {
const res = await billsList(this.filterElderId || null);

View File

@@ -4,9 +4,21 @@
<el-table :data="items" stripe>
<el-table-column prop="id" label="编号" width="80" />
<el-table-column prop="elderId" label="长者ID" />
<el-table-column prop="type" label="类型" />
<el-table-column label="类型" width="120">
<template slot-scope="scope">
<el-tag :type="typeTag(scope.row.type)">
{{ typeLabel(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="content" label="内容" />
<el-table-column prop="status" label="状态" width="120" />
<el-table-column label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="statusTag(scope.row.status)">
{{ statusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button size="mini" @click="openReply(scope.row)">回复</el-button>
@@ -50,6 +62,30 @@ export default {
this.load();
},
methods: {
typeLabel(type) {
if (type === "SUGGESTION") return "建议";
if (type === "COMPLAINT") return "投诉";
if (type === "PRAISE") return "表扬";
return type || "-";
},
typeTag(type) {
if (type === "SUGGESTION") return "info";
if (type === "COMPLAINT") return "danger";
if (type === "PRAISE") return "success";
return "info";
},
statusLabel(status) {
if (status === "NEW") return "新建";
if (status === "PROCESSING") return "处理中";
if (status === "CLOSED") return "已关闭";
return status || "-";
},
statusTag(status) {
if (status === "NEW") return "warning";
if (status === "PROCESSING") return "";
if (status === "CLOSED") return "success";
return "info";
},
async load() {
try {
const res = await feedbackList();

View File

@@ -7,7 +7,13 @@
<el-table :data="items" stripe>
<el-table-column prop="id" label="编号" width="80" />
<el-table-column prop="title" label="标题" />
<el-table-column prop="targetRole" label="对象" width="120" />
<el-table-column label="对象" width="120">
<template slot-scope="scope">
<el-tag :type="targetTag(scope.row.targetRole)">
{{ targetLabel(scope.row.targetRole) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" />
</el-table>
@@ -47,6 +53,18 @@ export default {
this.load();
},
methods: {
targetLabel(role) {
if (role === "ALL") return "全部";
if (role === "NURSE") return "护工";
if (role === "FAMILY") return "家属";
return role || "-";
},
targetTag(role) {
if (role === "ALL") return "";
if (role === "NURSE") return "success";
if (role === "FAMILY") return "info";
return "info";
},
async load() {
try {
const res = await noticeList();

View File

@@ -16,7 +16,13 @@
<el-table-column prop="id" label="编号" width="80" />
<el-table-column prop="username" label="账号" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="role" label="角色" width="100" />
<el-table-column label="角色" width="100">
<template slot-scope="scope">
<el-tag :type="roleTag(scope.row.role)">
{{ roleLabel(scope.row.role) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
@@ -89,6 +95,18 @@ export default {
this.loadUsers();
},
methods: {
roleLabel(role) {
if (role === "ADMIN") return "管理员";
if (role === "NURSE") return "护工";
if (role === "FAMILY") return "家属";
return role || "-";
},
roleTag(role) {
if (role === "ADMIN") return "danger";
if (role === "NURSE") return "success";
if (role === "FAMILY") return "info";
return "info";
},
async loadUsers() {
try {
const res = await adminUsers(this.role || null);

View File

@@ -9,7 +9,13 @@
<el-table :data="items" stripe>
<el-table-column prop="month" label="月份" />
<el-table-column prop="total" label="金额" />
<el-table-column prop="status" label="状态" />
<el-table-column label="状态" width="120">
<template slot-scope="scope">
<el-tag :type="statusTag(scope.row.status)">
{{ statusLabel(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="160">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="pay(scope.row)" :disabled="scope.row.status === 'PAID'">
@@ -37,6 +43,16 @@ export default {
}
},
methods: {
statusLabel(status) {
if (status === "PAID") return "已支付";
if (status === "UNPAID") return "未支付";
return status || "-";
},
statusTag(status) {
if (status === "PAID") return "success";
if (status === "UNPAID") return "warning";
return "info";
},
async load() {
if (!this.elderId) return;
try {