157 lines
7.0 KiB
Java
157 lines
7.0 KiB
Java
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 "手机用户";
|
||
}
|
||
}
|
||
|
||
|