Skip to content

Commit e569934

Browse files
committed
Optimized SQL transactions to ensure database consistency
1 parent 33a083b commit e569934

File tree

7 files changed

+191
-169
lines changed

7 files changed

+191
-169
lines changed

OpenBioCardServer/Controllers/AdminController.cs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,33 +84,46 @@ public async Task<ActionResult<UserListResponse>> GetUsers()
8484
[HttpPost("users")]
8585
public async Task<ActionResult<TokenResponse>> CreateUser([FromBody] CreateUserRequest request)
8686
{
87-
var token = GetTokenFromHeader();
88-
var (isValid, account) = await ValidateAdminTokenAsync(token);
89-
90-
if (!isValid || account == null)
87+
using var transaction = await _context.Database.BeginTransactionAsync();
88+
89+
try
9190
{
92-
return Unauthorized(new { error = "Invalid token or insufficient permissions" });
93-
}
91+
var token = GetTokenFromHeader();
92+
var (isValid, account) = await ValidateAdminTokenAsync(token);
93+
94+
if (!isValid || account == null)
95+
{
96+
return Unauthorized(new { error = "Invalid token or insufficient permissions" });
97+
}
9498

95-
if (!Enum.TryParse<UserType>(request.Type, true, out var userType) || userType == UserType.Root)
96-
{
97-
return BadRequest(new { error = "Invalid user type" });
98-
}
99+
if (!Enum.TryParse<UserType>(request.Type, true, out var userType) || userType == UserType.Root)
100+
{
101+
return BadRequest(new { error = "Invalid user type" });
102+
}
99103

100-
if (await _authService.UsernameExistsAsync(request.NewUsername))
101-
{
102-
return Conflict(new { error = "Username already exists" });
103-
}
104+
if (await _authService.UsernameExistsAsync(request.NewUsername))
105+
{
106+
return Conflict(new { error = "Username already exists" });
107+
}
104108

105-
var newAccount = await _authService.CreateAccountAsync(request.NewUsername, request.Password, userType);
106-
await _authService.CreateDefaultProfileAsync(newAccount.Id, request.NewUsername);
109+
var newAccount = await _authService.CreateAccountAsync(request.NewUsername, request.Password, userType);
110+
await _authService.CreateDefaultProfileAsync(newAccount.Id, request.NewUsername);
107111

108-
var newToken = await _authService.CreateTokenAsync(newAccount);
112+
var newToken = await _authService.CreateTokenAsync(newAccount);
109113

110-
_logger.LogInformation("Admin {AdminUser} created new user: {NewUser} (Type: {Type})",
111-
account.UserName, request.NewUsername, userType);
114+
await transaction.CommitAsync();
112115

113-
return Ok(new TokenResponse { Token = newToken });
116+
_logger.LogInformation("Admin {AdminUser} created new user: {NewUser} (Type: {Type})",
117+
account.UserName, request.NewUsername, userType);
118+
119+
return Ok(new TokenResponse { Token = newToken });
120+
}
121+
catch (Exception ex)
122+
{
123+
await transaction.RollbackAsync();
124+
_logger.LogError(ex, "Error creating new user");
125+
return StatusCode(500, new { error = "User creation failed" });
126+
}
114127
}
115128

116129
/// <summary>

OpenBioCardServer/Controllers/AuthController.cs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,53 @@ public AuthController(
3535
[EnableRateLimiting("login")]
3636
public async Task<ActionResult<TokenResponse>> SignUp([FromBody] SignUpRequest request)
3737
{
38-
if (string.IsNullOrWhiteSpace(request.Username) ||
39-
string.IsNullOrWhiteSpace(request.Password) ||
40-
string.IsNullOrWhiteSpace(request.UserType))
38+
using var transaction = await _context.Database.BeginTransactionAsync();
39+
40+
try
4141
{
42-
return BadRequest(new { error = "Missing required fields" });
43-
}
42+
if (string.IsNullOrWhiteSpace(request.Username) ||
43+
string.IsNullOrWhiteSpace(request.Password) ||
44+
string.IsNullOrWhiteSpace(request.UserType))
45+
{
46+
return BadRequest(new { error = "Missing required fields" });
47+
}
4448

45-
if (!Enum.TryParse<UserType>(request.UserType, true, out var userType))
46-
{
47-
return BadRequest(new { error = "Invalid user type" });
48-
}
49+
if (!Enum.TryParse<UserType>(request.UserType, true, out var userType))
50+
{
51+
return BadRequest(new { error = "Invalid user type" });
52+
}
4953

50-
if (userType == UserType.Root)
51-
{
52-
return Forbid("Cannot create root users");
53-
}
54+
if (userType == UserType.Root)
55+
{
56+
return Forbid("Cannot create root users");
57+
}
5458

55-
if (await _authService.UsernameExistsAsync(request.Username))
56-
{
57-
return Conflict(new { error = "Username already exists" });
58-
}
59+
if (await _authService.UsernameExistsAsync(request.Username))
60+
{
61+
return Conflict(new { error = "Username already exists" });
62+
}
63+
64+
var account = await _authService.CreateAccountAsync(request.Username, request.Password, userType);
65+
await _authService.CreateDefaultProfileAsync(account.Id, request.Username);
5966

60-
var account = await _authService.CreateAccountAsync(request.Username, request.Password, userType);
61-
await _authService.CreateDefaultProfileAsync(account.Id, request.Username);
67+
var token = await _authService.CreateTokenAsync(account);
6268

63-
var token = await _authService.CreateTokenAsync(account);
69+
await transaction.CommitAsync();
6470

65-
_logger.LogInformation("New user registered: {Username} (Type: {Type})",
66-
request.Username, userType);
71+
_logger.LogInformation("New user registered: {Username} (Type: {Type})",
72+
request.Username, userType);
6773

68-
return Ok(new TokenResponse { Token = token });
74+
return Ok(new TokenResponse { Token = token });
75+
}
76+
catch (Exception ex)
77+
{
78+
await transaction.RollbackAsync();
79+
_logger.LogError(ex, "Error during user registration");
80+
return StatusCode(500, new { error = "Account creation failed" });
81+
}
6982
}
7083

84+
7185
/// <summary>
7286
/// 用户登录
7387
/// </summary>

OpenBioCardServer/Controllers/Classic/ClassicAdminController.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ private async Task<IActionResult> GetUsersInternal(string token, string username
134134
[HttpPost("users")]
135135
public async Task<IActionResult> CreateUser([FromBody] ClassicCreateUserRequest request)
136136
{
137+
using var transaction = await _context.Database.BeginTransactionAsync();
138+
137139
try
138140
{
139141
var (isValid, account) = await _authService.ValidateTokenAsync(request.Token);
@@ -199,6 +201,8 @@ public async Task<IActionResult> CreateUser([FromBody] ClassicCreateUserRequest
199201
// Generate token for new user
200202
var newToken = await _authService.CreateTokenAsync(newAccount);
201203

204+
await transaction.CommitAsync();
205+
202206
_logger.LogInformation("Admin {AdminUser} created new user: {NewUser} (Type: {Type})",
203207
request.Username, request.NewUsername, userType);
204208

@@ -210,6 +214,7 @@ public async Task<IActionResult> CreateUser([FromBody] ClassicCreateUserRequest
210214
}
211215
catch (Exception ex)
212216
{
217+
await transaction.RollbackAsync();
213218
_logger.LogError(ex, "Error creating new user");
214219
return StatusCode(500, new { error = "User creation failed" });
215220
}

OpenBioCardServer/Controllers/Classic/ClassicAuthController.cs

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public ClassicAuthController(
3838
[EnableRateLimiting("login")]
3939
public async Task<IActionResult> SignUp([FromBody] ClassicSignUpRequest request)
4040
{
41+
using var transaction = await _context.Database.BeginTransactionAsync();
42+
4143
try
4244
{
4345
// Validate required fields
@@ -94,13 +96,16 @@ public async Task<IActionResult> SignUp([FromBody] ClassicSignUpRequest request)
9496
// Generate authentication token
9597
var token = await _authService.CreateTokenAsync(account);
9698

99+
await transaction.CommitAsync();
100+
97101
_logger.LogInformation("New user registered: {Username} (Type: {Type})",
98102
request.Username, userType);
99103

100104
return Ok(new ClassicTokenResponse { Token = token });
101105
}
102106
catch (Exception ex)
103107
{
108+
await transaction.RollbackAsync();
104109
_logger.LogError(ex, "Error during user registration");
105110
return StatusCode(500, new { error = "Account creation failed" });
106111
}
@@ -206,54 +211,4 @@ public async Task<IActionResult> DeleteAccount([FromBody] ClassicDeleteRequest r
206211
return StatusCode(500, new { error = "Account deletion failed" });
207212
}
208213
}
209-
210-
/// <summary>
211-
/// Initialize admin user (for first-time setup only)
212-
/// </summary>
213-
[HttpGet("init-admin")]
214-
public async Task<IActionResult> InitAdmin()
215-
{
216-
try
217-
{
218-
// Only allow if no users exist yet
219-
if (await _context.Accounts.AnyAsync())
220-
{
221-
return Ok("Admin already initialized");
222-
}
223-
224-
// Create default admin account
225-
var (hash, salt) = PasswordHasher.HashPassword("admin");
226-
var admin = new Account
227-
{
228-
UserName = "admin",
229-
PasswordHash = hash,
230-
PasswordSalt = salt,
231-
Type = UserType.Admin
232-
};
233-
234-
_context.Accounts.Add(admin);
235-
await _context.SaveChangesAsync();
236-
237-
// Create default profile
238-
var profile = new ProfileEntity
239-
{
240-
AccountId = admin.Id,
241-
Username = "admin",
242-
AvatarType = AssetType.Text,
243-
AvatarText = "👤"
244-
};
245-
246-
_context.Profiles.Add(profile);
247-
await _context.SaveChangesAsync();
248-
249-
_logger.LogInformation("Admin user initialized");
250-
251-
return Ok("Admin initialized");
252-
}
253-
catch (Exception ex)
254-
{
255-
_logger.LogError(ex, "Error during admin initialization");
256-
return StatusCode(500, new { error = "Initialization failed" });
257-
}
258-
}
259214
}

OpenBioCardServer/Controllers/Classic/ClassicUserController.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public async Task<IActionResult> GetProfile(string username)
6565
[HttpPost("{username}")]
6666
public async Task<IActionResult> UpdateProfile(string username, [FromBody] ClassicProfile request)
6767
{
68+
using var transaction = await _context.Database.BeginTransactionAsync();
69+
6870
try
6971
{
7072
// Extract token from Authorization header
@@ -105,15 +107,7 @@ public async Task<IActionResult> UpdateProfile(string username, [FromBody] Class
105107
// Update basic profile fields
106108
ClassicMapper.UpdateProfileFromClassic(profile, request);
107109

108-
// Clear all existing collections (we'll replace them completely)
109-
// _context.ContactItems.RemoveRange(profile.Contacts);
110-
// _context.SocialLinkItems.RemoveRange(profile.SocialLinks);
111-
// _context.ProjectItems.RemoveRange(profile.Projects);
112-
// _context.WorkExperienceItems.RemoveRange(profile.WorkExperiences);
113-
// _context.SchoolExperienceItems.RemoveRange(profile.SchoolExperiences);
114-
// _context.GalleryItems.RemoveRange(profile.Gallery);
115-
116-
110+
// Clear all existing collections using ExecuteDeleteAsync
117111
await _context.ContactItems
118112
.Where(c => c.ProfileId == profile.Id)
119113
.ExecuteDeleteAsync();
@@ -176,13 +170,15 @@ await _context.GalleryItems
176170
}
177171

178172
await _context.SaveChangesAsync();
173+
await transaction.CommitAsync();
179174

180175
_logger.LogInformation("Profile updated for user: {Username}", username);
181176

182177
return Ok(new { success = true });
183178
}
184179
catch (Exception ex)
185180
{
181+
await transaction.RollbackAsync();
186182
_logger.LogError(ex, "Error updating profile for user: {Username}", username);
187183
return StatusCode(500, new { error = "Profile update failed" });
188184
}

OpenBioCardServer/Controllers/ProfileController.cs

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,40 +56,53 @@ public async Task<ActionResult<ProfileDto>> GetProfile(string username)
5656
[HttpPut("{username}")]
5757
public async Task<IActionResult> UpdateProfile(string username, [FromBody] ProfileDto request)
5858
{
59-
var token = GetTokenFromHeader();
60-
var (isValid, account) = await ValidateTokenAndUser(token, username);
61-
62-
if (!isValid || account == null)
59+
using var transaction = await _context.Database.BeginTransactionAsync();
60+
61+
try
6362
{
64-
return Unauthorized(new { error = "Invalid token or token does not match username" });
63+
var token = GetTokenFromHeader();
64+
var (isValid, account) = await ValidateTokenAndUser(token, username);
65+
66+
if (!isValid || account == null)
67+
{
68+
return Unauthorized(new { error = "Invalid token or token does not match username" });
69+
}
70+
71+
var profile = await _context.Profiles
72+
.Include(p => p.Contacts)
73+
.Include(p => p.SocialLinks)
74+
.Include(p => p.Projects)
75+
.Include(p => p.WorkExperiences)
76+
.Include(p => p.SchoolExperiences)
77+
.Include(p => p.Gallery)
78+
.FirstOrDefaultAsync(p => p.Username == username);
79+
80+
if (profile == null)
81+
{
82+
return NotFound(new { error = "Profile not found" });
83+
}
84+
85+
// 更新基本资料
86+
DataMapper.UpdateProfileEntity(profile, request);
87+
88+
// 清除并替换所有子项
89+
await ReplaceCollectionItemsAsync(profile, request);
90+
91+
await _context.SaveChangesAsync();
92+
await transaction.CommitAsync();
93+
94+
_logger.LogInformation("Profile updated for user: {Username}", username);
95+
return Ok(new { success = true });
6596
}
66-
67-
var profile = await _context.Profiles
68-
.Include(p => p.Contacts)
69-
.Include(p => p.SocialLinks)
70-
.Include(p => p.Projects)
71-
.Include(p => p.WorkExperiences)
72-
.Include(p => p.SchoolExperiences)
73-
.Include(p => p.Gallery)
74-
.FirstOrDefaultAsync(p => p.Username == username);
75-
76-
if (profile == null)
97+
catch (Exception ex)
7798
{
78-
return NotFound(new { error = "Profile not found" });
99+
await transaction.RollbackAsync();
100+
_logger.LogError(ex, "Error updating profile for user: {Username}", username);
101+
return StatusCode(500, new { error = "Profile update failed" });
79102
}
80-
81-
// 更新基本资料
82-
DataMapper.UpdateProfileEntity(profile, request);
83-
84-
// 清除并替换所有子项
85-
await ReplaceCollectionItemsAsync(profile, request);
86-
87-
await _context.SaveChangesAsync();
88-
89-
_logger.LogInformation("Profile updated for user: {Username}", username);
90-
return Ok(new { success = true });
91103
}
92104

105+
93106
/// <summary>
94107
/// 获取当前登录用户的资料(私有)
95108
/// </summary>

0 commit comments

Comments
 (0)