Files
PartsInquiry/backend/src/main/java/com/example/demo/user/UserService.java
2025-09-27 22:57:59 +08:00

137 lines
5.8 KiB
Java

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("手机号格式不正确");
}
}