2
This commit is contained in:
136
backend/src/main/java/com/example/demo/user/UserService.java
Normal file
136
backend/src/main/java/com/example/demo/user/UserService.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.example.demo.user;
|
||||
|
||||
import com.example.demo.attachment.AttachmentUrlValidator;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.crypto.bcrypt.BCrypt;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final AttachmentUrlValidator urlValidator;
|
||||
|
||||
public UserService(JdbcTemplate jdbcTemplate, AttachmentUrlValidator urlValidator) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.urlValidator = urlValidator;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Map<String, Object> getProfile(Long userId) {
|
||||
return jdbcTemplate.query(
|
||||
con -> {
|
||||
var ps = con.prepareStatement("SELECT id, shop_id, phone, email, name, avatar_url FROM users WHERE id=? LIMIT 1");
|
||||
ps.setLong(1, userId);
|
||||
return ps;
|
||||
},
|
||||
rs -> {
|
||||
if (!rs.next()) return null;
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("id", rs.getLong("id"));
|
||||
m.put("shopId", rs.getLong("shop_id"));
|
||||
m.put("phone", rs.getString("phone"));
|
||||
m.put("email", rs.getString("email"));
|
||||
m.put("name", rs.getString("name"));
|
||||
m.put("avatarUrl", rs.getString("avatar_url"));
|
||||
return m;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static class UpdateProfileRequest { public String name; public String avatarUrl; }
|
||||
|
||||
@Transactional
|
||||
public void updateProfile(Long userId, UpdateProfileRequest req) {
|
||||
if ((req.name == null || req.name.isBlank()) && (req.avatarUrl == null || req.avatarUrl.isBlank())) {
|
||||
return; // nothing to update
|
||||
}
|
||||
StringBuilder sql = new StringBuilder("UPDATE users SET ");
|
||||
java.util.List<Object> args = new java.util.ArrayList<>();
|
||||
boolean first = true;
|
||||
if (req.name != null) {
|
||||
String nm = req.name.trim();
|
||||
if (nm.isEmpty()) throw new IllegalArgumentException("姓名不能为空");
|
||||
if (nm.length() > 64) throw new IllegalArgumentException("姓名过长");
|
||||
sql.append(first ? "name=?" : ", name=?");
|
||||
args.add(nm);
|
||||
first = false;
|
||||
}
|
||||
if (req.avatarUrl != null) {
|
||||
String au = req.avatarUrl.trim();
|
||||
if (au.isEmpty()) {
|
||||
sql.append(first ? "avatar_url=NULL" : ", avatar_url=NULL");
|
||||
} else if (au.startsWith("/")) {
|
||||
String normalized = au.replaceAll("/{2,}", "/");
|
||||
sql.append(first ? "avatar_url=?" : ", avatar_url=?");
|
||||
args.add(normalized);
|
||||
} else {
|
||||
var vr = urlValidator.validate(au);
|
||||
sql.append(first ? "avatar_url=?" : ", avatar_url=?");
|
||||
args.add(vr.url());
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
sql.append(", updated_at=NOW() WHERE id=?");
|
||||
args.add(userId);
|
||||
jdbcTemplate.update(sql.toString(), args.toArray());
|
||||
}
|
||||
|
||||
public static class ChangePasswordRequest { public String oldPassword; public String newPassword; }
|
||||
|
||||
@Transactional
|
||||
public void changePassword(Long userId, ChangePasswordRequest req) {
|
||||
if (req.newPassword == null || req.newPassword.isBlank()) throw new IllegalArgumentException("新密码不能为空");
|
||||
if (req.newPassword.length() < 6) throw new IllegalArgumentException("新密码至少6位");
|
||||
Map<String, Object> row = jdbcTemplate.query(
|
||||
con -> {
|
||||
var ps = con.prepareStatement("SELECT password_hash FROM users WHERE id=?");
|
||||
ps.setLong(1, userId);
|
||||
return ps;
|
||||
},
|
||||
rs -> {
|
||||
if (rs.next()) {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("password_hash", rs.getString(1));
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
if (row == null) throw new IllegalArgumentException("用户不存在");
|
||||
String existing = (String) row.get("password_hash");
|
||||
if (existing != null && !existing.isBlank()) {
|
||||
if (req.oldPassword == null || req.oldPassword.isBlank()) throw new IllegalArgumentException("请提供旧密码");
|
||||
boolean ok = BCrypt.checkpw(req.oldPassword, existing);
|
||||
if (!ok) throw new IllegalArgumentException("旧密码不正确");
|
||||
}
|
||||
String newHash = BCrypt.hashpw(req.newPassword, BCrypt.gensalt());
|
||||
jdbcTemplate.update("UPDATE users SET password_hash=?, updated_at=NOW() WHERE id=?", newHash, userId);
|
||||
}
|
||||
|
||||
public static class ChangePhoneRequest { public String phone; public String code; }
|
||||
|
||||
@Transactional
|
||||
public void changePhone(Long userId, ChangePhoneRequest req) {
|
||||
ensurePhoneFormat(req.phone);
|
||||
// 无需验证码,直接保存;保留唯一性与格式校验
|
||||
try {
|
||||
jdbcTemplate.update("UPDATE users SET phone=?, updated_at=NOW() WHERE id=?", req.phone, userId);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
throw new IllegalArgumentException("该手机号已被占用");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensurePhoneFormat(String phone) {
|
||||
if (phone == null || phone.isBlank()) throw new IllegalArgumentException("手机号不能为空");
|
||||
String p = phone.replaceAll("\\s+", "");
|
||||
if (!p.matches("^1\\d{10}$")) throw new IllegalArgumentException("手机号格式不正确");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user