Files
PartsInquiry/backend/src/main/java/com/example/demo/auth/RegisterService.java
2025-09-24 20:35:15 +08:00

157 lines
7.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.example.demo.auth;
import com.example.demo.common.AppDefaultsProperties;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.demo.common.ShopDefaultsProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.Map;
@Service
public class RegisterService {
private final JdbcTemplate jdbcTemplate;
private final JwtService jwtService;
private final JwtProperties jwtProps;
private final ShopDefaultsProperties shopDefaults;
private AppDefaultsProperties appDefaults;
public RegisterService(JdbcTemplate jdbcTemplate,
JwtService jwtService,
JwtProperties jwtProps,
ShopDefaultsProperties shopDefaults) {
this.jdbcTemplate = jdbcTemplate;
this.jwtService = jwtService;
this.jwtProps = jwtProps;
this.shopDefaults = shopDefaults;
}
@Autowired
public void setAppDefaults(AppDefaultsProperties appDefaults) {
this.appDefaults = appDefaults;
}
private String hashPassword(String raw) {
try {
return org.springframework.security.crypto.bcrypt.BCrypt.hashpw(raw, org.springframework.security.crypto.bcrypt.BCrypt.gensalt(10));
} catch (Exception e) {
throw new IllegalStateException("密码加密失败", e);
}
}
public static class RegisterRequest {
public String phone; // 必填11位
public String name; // 可选:默认用脱敏手机号
public String password; // 可选:如提供则保存密码哈希
}
public static class RegisterResponse {
public String token;
public long expiresIn;
public Map<String,Object> user;
}
@Transactional
public RegisterResponse register(RegisterRequest req) {
ensurePhoneFormat(req.phone);
String phone = req.phone.trim();
String displayName = (req.name == null || req.name.isBlank()) ? maskPhoneForName(phone) : req.name.trim();
// 已存在则直接签发令牌
var existing = jdbcTemplate.queryForList("SELECT id, shop_id FROM users WHERE phone=? LIMIT 1", phone);
Long userId;
Long shopId;
if (!existing.isEmpty()) {
Map<String,Object> row = existing.get(0);
userId = ((Number)row.get("id")).longValue();
shopId = ((Number)row.get("shop_id")).longValue();
} else {
// 1) 创建店铺
String shopName = String.format(shopDefaults.getNamePattern(), displayName);
GeneratedKeyHolder shopKey = new GeneratedKeyHolder();
jdbcTemplate.update(con -> {
PreparedStatement ps = con.prepareStatement(
"INSERT INTO shops(name, status, created_at, updated_at) VALUES (?,1,NOW(),NOW())",
java.sql.Statement.RETURN_GENERATED_KEYS);
ps.setString(1, shopName);
return ps;
}, shopKey);
Number shopGenId = shopKey.getKey();
if (shopGenId == null) throw new IllegalStateException("创建店铺失败");
shopId = shopGenId.longValue();
// 2) 创建店主用户owner
GeneratedKeyHolder userKey = new GeneratedKeyHolder();
final Long sid = shopId;
jdbcTemplate.update(con -> {
PreparedStatement ps = con.prepareStatement(
"INSERT INTO users(shop_id, phone, name, role, password_hash, status, is_owner, created_at, updated_at) " +
"VALUES (?,?,?,?,?,1,1,NOW(),NOW())",
java.sql.Statement.RETURN_GENERATED_KEYS);
ps.setLong(1, sid);
ps.setString(2, phone);
ps.setString(3, displayName);
ps.setString(4, shopDefaults.getOwnerRole());
// 如提供密码,存入哈希;否则设为 NULL
String pwd = (req.password == null || req.password.isBlank()) ? null : hashPassword(req.password);
if (pwd != null) ps.setString(5, pwd); else ps.setNull(5, java.sql.Types.VARCHAR);
return ps;
}, userKey);
Number userGenId = userKey.getKey();
if (userGenId == null) throw new IllegalStateException("创建用户失败");
userId = userGenId.longValue();
// 3) 创建默认账户(现金/银行存款/微信)
createDefaultAccounts(shopId, userId);
}
String token = jwtService.signToken(userId, shopId, phone, "register");
RegisterResponse out = new RegisterResponse();
out.token = token;
out.expiresIn = jwtProps.getTtlSeconds();
HashMap<String,Object> userMap = new HashMap<>();
userMap.put("userId", userId);
userMap.put("shopId", shopId);
userMap.put("phone", phone);
out.user = userMap;
return out;
}
private void createDefaultAccounts(Long shopId, Long userId) {
// 现金
jdbcTemplate.update(
"INSERT INTO accounts(shop_id,user_id,name,`type`,balance,status,created_at,updated_at) " +
"SELECT ?, ?, ?, 'cash', 0, 1, NOW(), NOW() FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM accounts WHERE shop_id=? AND name=?)",
shopId, userId, appDefaults.getAccountCashName(), shopId, appDefaults.getAccountCashName());
// 银行存款
jdbcTemplate.update(
"INSERT INTO accounts(shop_id,user_id,name,`type`,balance,status,created_at,updated_at) " +
"SELECT ?, ?, ?, 'bank', 0, 1, NOW(), NOW() FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM accounts WHERE shop_id=? AND name=?)",
shopId, userId, appDefaults.getAccountBankName(), shopId, appDefaults.getAccountBankName());
// 微信
jdbcTemplate.update(
"INSERT INTO accounts(shop_id,user_id,name,`type`,balance,status,created_at,updated_at) " +
"SELECT ?, ?, ?, 'wechat', 0, 1, NOW(), NOW() FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM accounts WHERE shop_id=? AND name=?)",
shopId, userId, appDefaults.getAccountWechatName(), shopId, appDefaults.getAccountWechatName());
}
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("手机号格式不正确");
}
private static String maskPhoneForName(String phone) {
String p = String.valueOf(phone);
if (p.length() == 11) return "用户" + p.substring(0,3) + "****" + p.substring(7);
return "手机用户";
}
}