- 预约详情: 隐藏用户手机、修复性别/年级映射、添加家庭氛围和期望字段、格式化日期时间 - 邀请页面: 规则弹窗文字间距修复、提现记录红色标题栏、邀请记录红色标题栏+边框、提现记录弹窗列宽优化 - 系统配置: 邀请规则改为多行文本框 - 邀请绑定: 添加前后端完整链路日志用于排查上下级绑定问题 - 首页: 专业测评区域改为横向滚动、更多区域改为全图片模式
This commit is contained in:
parent
e5e63bd7f2
commit
c22a743eb0
|
|
@ -84,4 +84,14 @@ public class BookingDetailDto : BookingDto
|
|||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 家庭氛围
|
||||
/// </summary>
|
||||
public string? FamilyAtmosphere { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 期望
|
||||
/// </summary>
|
||||
public string? Expectation { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public class PlannerService : IPlannerService
|
|||
/// </summary>
|
||||
private static readonly Dictionary<int, string> GenderNames = new()
|
||||
{
|
||||
{ 0, "未填写" },
|
||||
{ 1, "男" },
|
||||
{ 2, "女" }
|
||||
};
|
||||
|
|
@ -51,18 +52,12 @@ public class PlannerService : IPlannerService
|
|||
/// </summary>
|
||||
private static readonly Dictionary<int, string> GradeNames = new()
|
||||
{
|
||||
{ 1, "一年级" },
|
||||
{ 2, "二年级" },
|
||||
{ 3, "三年级" },
|
||||
{ 4, "四年级" },
|
||||
{ 5, "五年级" },
|
||||
{ 6, "六年级" },
|
||||
{ 7, "初一" },
|
||||
{ 8, "初二" },
|
||||
{ 9, "初三" },
|
||||
{ 10, "高一" },
|
||||
{ 11, "高二" },
|
||||
{ 12, "高三" }
|
||||
{ 1, "小学" },
|
||||
{ 2, "初中" },
|
||||
{ 3, "高中" },
|
||||
{ 4, "大专" },
|
||||
{ 5, "本科" },
|
||||
{ 6, "研究生及以上" }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -405,6 +400,8 @@ public class PlannerService : IPlannerService
|
|||
ScoreBiology = booking.ScoreBiology,
|
||||
ScoreGeography = booking.ScoreGeography,
|
||||
ScorePolitics = booking.ScorePolitics,
|
||||
FamilyAtmosphere = booking.FamilyAtmosphere,
|
||||
Expectation = booking.Expectation,
|
||||
Status = booking.Status,
|
||||
StatusName = GetBookingStatusName(booking.Status),
|
||||
OrderAmount = booking.Order?.Amount,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ export interface BookingDetail extends BookingItem {
|
|||
scoreBiology: number | null
|
||||
scoreGeography: number | null
|
||||
scorePolitics: number | null
|
||||
familyAtmosphere: string | null
|
||||
expectation: string | null
|
||||
plannerIntroduction: string
|
||||
orderAmount: number | null
|
||||
orderStatus: number | null
|
||||
|
|
|
|||
|
|
@ -98,6 +98,14 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="配置值">
|
||||
<el-input
|
||||
v-if="isTextareaConfig(state.editingItem?.configKey || '')"
|
||||
v-model="state.editValue"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
:placeholder="getPlaceholder(state.editingItem?.configType || '')"
|
||||
/>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="state.editValue"
|
||||
:placeholder="getPlaceholder(state.editingItem?.configType || '')"
|
||||
clearable
|
||||
|
|
@ -201,6 +209,9 @@ const RICH_TEXT_CONFIG_KEYS = ['user_agreement', 'privacy_policy', 'about_us_con
|
|||
// 图片配置键列表
|
||||
const IMAGE_CONFIG_KEYS = ['service_qrcode']
|
||||
|
||||
// 多行文本配置键列表
|
||||
const TEXTAREA_CONFIG_KEYS = ['invite_rule']
|
||||
|
||||
// 隐藏的配置键列表(暂未实际使用的配置项)
|
||||
const HIDDEN_CONFIG_KEYS = ['service_phone', 'service_wechat', 'about_us_content', 'assessment_price']
|
||||
|
||||
|
|
@ -230,6 +241,13 @@ function isRichTextConfig(configKey: string): boolean {
|
|||
return RICH_TEXT_CONFIG_KEYS.includes(configKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为多行文本配置
|
||||
*/
|
||||
function isTextareaConfig(configKey: string): boolean {
|
||||
return TEXTAREA_CONFIG_KEYS.includes(configKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为图片配置
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@
|
|||
<el-descriptions-item label="订单号">{{ detailData.orderNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户UID">{{ detailData.userUid }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户昵称">{{ detailData.userNickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户手机">{{ detailData.userPhone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="规划师">
|
||||
<div class="planner-info">
|
||||
<el-avatar :src="detailData.plannerAvatar" :size="40" />
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="预约日期">{{ detailData.bookingDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约日期">{{ formatBookingDateTime(detailData.bookingDate, detailData.bookingTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学生姓名">{{ detailData.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ detailData.genderName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学生年级">{{ detailData.gradeName }}</el-descriptions-item>
|
||||
|
|
@ -126,10 +125,12 @@
|
|||
<el-descriptions-item label="生物成绩">{{ detailData.scoreBiology ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="地理成绩">{{ detailData.scoreGeography ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="政治成绩">{{ detailData.scorePolitics ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="家庭氛围">{{ detailData.familyAtmosphere || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="期望">{{ detailData.expectation || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="getStatusType(detailData.status)">{{ detailData.statusName }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatDateTime(detailData.createTime) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-drawer>
|
||||
|
||||
|
|
@ -211,6 +212,22 @@ const statusForm = reactive({
|
|||
status: 0
|
||||
})
|
||||
|
||||
// 状态类型映射
|
||||
const getStatusType = (status: number): '' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
|
||||
// 格式化日期时间:去掉T,秒数不带小数点
|
||||
const formatDateTime = (dt: string | undefined): string => {
|
||||
if (!dt) return '-'
|
||||
return dt.substring(0, 19).replace('T', ' ')
|
||||
}
|
||||
|
||||
// 格式化预约日期+时间
|
||||
const formatBookingDateTime = (date: string | undefined, time: string | undefined): string => {
|
||||
if (!date) return '-'
|
||||
const d = date.substring(0, 10)
|
||||
return time ? `${d} ${time}` : d
|
||||
}
|
||||
|
||||
// 状态类型映射
|
||||
const getStatusType = (status: number): '' | 'success' | 'warning' | 'info' | 'danger' => {
|
||||
const map: Record<number, '' | 'success' | 'warning' | 'info' | 'danger'> = {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ using Microsoft.AspNetCore.Mvc;
|
|||
namespace MiAssessment.Api.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 认证控制器 - 处理用户登录、注册、Token刷新和手机号绑定
|
||||
/// <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> - <20><><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD>¼<EFBFBD><C2BC>ע<EFBFBD>ᡢTokenˢ<6E>º<EFBFBD><C2BA>ֻ<EFBFBD><D6BB>Ű<EFBFBD>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 提供微信小程序登录、手机号验证码登录、Token刷新、手机号绑定等功能
|
||||
/// <EFBFBD>ṩ<EFBFBD><EFBFBD>С<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD>Tokenˢ<EFBFBD>¡<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>Űȹ<EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </remarks>
|
||||
[ApiController]
|
||||
[Route("api")]
|
||||
|
|
@ -29,23 +29,26 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信小程序登录
|
||||
/// <EFBFBD><EFBFBD>С<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/login
|
||||
///
|
||||
/// 使用微信小程序授权code进行登录,返回双Token(Access Token + Refresh Token)
|
||||
/// ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩcode<EFBFBD><EFBFBD><EFBFBD>е<EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˫Token<EFBFBD><EFBFBD>Access Token + Refresh Token<65><6E>
|
||||
/// Requirements: 1.1, 1.2, 6.1
|
||||
/// </remarks>
|
||||
/// <param name="request">微信登录请求参数,包含授权code</param>
|
||||
/// <returns>登录成功返回LoginResponse(包含accessToken、refreshToken、expiresIn),失败返回错误信息</returns>
|
||||
/// <param name="request"><EFBFBD>ŵ<EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩcode</param>
|
||||
/// <returns><EFBFBD><EFBFBD>¼<EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>LoginResponse<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>accessToken<EFBFBD><EFBFBD>refreshToken<EFBFBD><EFBFBD>expiresIn<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ش<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("login")]
|
||||
[ProducesResponseType(typeof(ApiResponse<LoginResponse>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<LoginResponse>> WechatMiniProgramLogin([FromBody] WechatLoginRequest request)
|
||||
{
|
||||
_logger.LogInformation("[AuthController] 收到登录请求: PidStr={PidStr}, Pid={Pid}, ClickId={ClickId}",
|
||||
request?.PidStr, request?.Pid, request?.ClickId);
|
||||
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.Code))
|
||||
{
|
||||
return ApiResponse<LoginResponse>.Fail("授权code不能为空");
|
||||
return ApiResponse<LoginResponse>.Fail("<EFBFBD><EFBFBD>Ȩcode<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
var result = await _authService.WechatMiniProgramLoginAsync(
|
||||
|
|
@ -56,41 +59,41 @@ public class AuthController : ControllerBase
|
|||
if (result.Success && result.LoginResponse != null)
|
||||
{
|
||||
_logger.LogInformation("WeChat login successful: UserId={UserId}", result.UserId);
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "登录成功");
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "<EFBFBD><EFBFBD>¼<EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
|
||||
_logger.LogWarning("WeChat login failed: {Error}", result.ErrorMessage);
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "登录失败");
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "<EFBFBD><EFBFBD>¼ʧ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手机号验证码登录
|
||||
/// <EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD>¼
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/mobileLogin
|
||||
///
|
||||
/// 使用手机号和验证码进行登录,返回双Token(Access Token + Refresh Token)
|
||||
/// ʹ<EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ź<EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˫Token<EFBFBD><EFBFBD>Access Token + Refresh Token<65><6E>
|
||||
/// Requirements: 1.1, 1.2, 6.1
|
||||
/// </remarks>
|
||||
/// <param name="request">手机号登录请求参数,包含手机号和验证码</param>
|
||||
/// <returns>登录成功返回LoginResponse(包含accessToken、refreshToken、expiresIn),失败返回错误信息</returns>
|
||||
/// <param name="request"><EFBFBD>ֻ<EFBFBD><EFBFBD>ŵ<EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ź<EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD></param>
|
||||
/// <returns><EFBFBD><EFBFBD>¼<EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>LoginResponse<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>accessToken<EFBFBD><EFBFBD>refreshToken<EFBFBD><EFBFBD>expiresIn<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ش<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("mobileLogin")]
|
||||
[ProducesResponseType(typeof(ApiResponse<LoginResponse>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<LoginResponse>> MobileLogin([FromBody] MobileLoginRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return ApiResponse<LoginResponse>.Fail("请求参数不能为空");
|
||||
return ApiResponse<LoginResponse>.Fail("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Mobile))
|
||||
{
|
||||
return ApiResponse<LoginResponse>.Fail("手机号不能为空");
|
||||
return ApiResponse<LoginResponse>.Fail("<EFBFBD>ֻ<EFBFBD><EFBFBD>Ų<EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Code))
|
||||
{
|
||||
return ApiResponse<LoginResponse>.Fail("验证码不能为空");
|
||||
return ApiResponse<LoginResponse>.Fail("<EFBFBD><EFBFBD>֤<EFBFBD>벻<EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
var result = await _authService.MobileLoginAsync(
|
||||
|
|
@ -102,24 +105,24 @@ public class AuthController : ControllerBase
|
|||
if (result.Success && result.LoginResponse != null)
|
||||
{
|
||||
_logger.LogInformation("Mobile login successful: UserId={UserId}", result.UserId);
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "登录成功");
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "<EFBFBD><EFBFBD>¼<EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
|
||||
_logger.LogWarning("Mobile login failed: {Error}", result.ErrorMessage);
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "登录失败");
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "<EFBFBD><EFBFBD>¼ʧ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新 Token
|
||||
/// ˢ<EFBFBD><EFBFBD> Token
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/refresh
|
||||
///
|
||||
/// 使用 Refresh Token 获取新的 Access Token 和 Refresh Token
|
||||
/// ʹ<EFBFBD><EFBFBD> Refresh Token <20><>ȡ<EFBFBD>µ<EFBFBD> Access Token <20><> Refresh Token
|
||||
/// Requirements: 2.1, 2.2, 2.3, 2.4
|
||||
/// </remarks>
|
||||
/// <param name="request">刷新请求,包含 Refresh Token</param>
|
||||
/// <returns>刷新成功返回新的 LoginResponse,失败返回错误信息</returns>
|
||||
/// <param name="request">ˢ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Refresh Token</param>
|
||||
/// <returns>ˢ<EFBFBD>³ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD> LoginResponse<73><65>ʧ<EFBFBD>ܷ<EFBFBD><DCB7>ش<EFBFBD><D8B4><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("refresh")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(ApiResponse<LoginResponse>), StatusCodes.Status200OK)]
|
||||
|
|
@ -127,7 +130,7 @@ public class AuthController : ControllerBase
|
|||
{
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.RefreshToken))
|
||||
{
|
||||
return ApiResponse<LoginResponse>.Fail("刷新令牌不能为空");
|
||||
return ApiResponse<LoginResponse>.Fail("ˢ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʋ<EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
var clientIp = GetClientIp();
|
||||
|
|
@ -136,27 +139,27 @@ public class AuthController : ControllerBase
|
|||
if (result.Success && result.LoginResponse != null)
|
||||
{
|
||||
_logger.LogInformation("Token refresh successful: UserId={UserId}", result.LoginResponse.UserId);
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "刷新成功");
|
||||
return ApiResponse<LoginResponse>.Success(result.LoginResponse, "ˢ<EFBFBD>³ɹ<EFBFBD>");
|
||||
}
|
||||
|
||||
_logger.LogWarning("Token refresh failed: {Error}", result.ErrorMessage);
|
||||
|
||||
// 根据错误类型返回不同的状态码
|
||||
// -1 表示未登录/Token无效,前端需要跳转登录页
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "刷新失败", -1);
|
||||
// <EFBFBD><EFBFBD><EFBFBD>ݴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD><EFBFBD>ز<EFBFBD>ͬ<EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD>
|
||||
// -1 <EFBFBD><EFBFBD>ʾδ<EFBFBD><EFBFBD>¼/Token<65><6E>Ч<EFBFBD><D0A7>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA>ת<EFBFBD><D7AA>¼ҳ
|
||||
return ApiResponse<LoginResponse>.Fail(result.ErrorMessage ?? "ˢ<EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>", -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 退出登录(撤销 Token)
|
||||
/// <EFBFBD>˳<EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Token<65><6E>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/logout
|
||||
///
|
||||
/// 撤销用户的 Refresh Token,使其失效
|
||||
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD> Refresh Token<65><6E>ʹ<EFBFBD><CAB9>ʧЧ
|
||||
/// Requirements: 4.4
|
||||
/// </remarks>
|
||||
/// <param name="request">退出请求,包含要撤销的 Refresh Token(可选,不传则撤销当前用户所有Token)</param>
|
||||
/// <returns>退出成功返回成功消息</returns>
|
||||
/// <param name="request"><EFBFBD>˳<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Refresh Token<65><6E><EFBFBD><EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD>Token<65><6E></param>
|
||||
/// <returns><EFBFBD>˳<EFBFBD><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>سɹ<EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("logout")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)]
|
||||
|
|
@ -169,38 +172,38 @@ public class AuthController : ControllerBase
|
|||
{
|
||||
if (request != null && !string.IsNullOrWhiteSpace(request.RefreshToken))
|
||||
{
|
||||
// 撤销指定的 Refresh Token
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Refresh Token
|
||||
await _authService.RevokeTokenAsync(request.RefreshToken, clientIp);
|
||||
_logger.LogInformation("Token revoked: UserId={UserId}", userId);
|
||||
}
|
||||
else if (userId.HasValue)
|
||||
{
|
||||
// 撤销用户的所有 Refresh Token
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Refresh Token
|
||||
await _authService.RevokeAllUserTokensAsync(userId.Value, clientIp);
|
||||
_logger.LogInformation("All tokens revoked: UserId={UserId}", userId);
|
||||
}
|
||||
|
||||
return ApiResponse.Success("退出成功");
|
||||
return ApiResponse.Success("<EFBFBD>˳<EFBFBD><EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Logout failed: UserId={UserId}, Error={Error}", userId, ex.Message);
|
||||
return ApiResponse.Fail("退出失败");
|
||||
return ApiResponse.Fail("<EFBFBD>˳<EFBFBD>ʧ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 微信授权绑定手机号
|
||||
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/login_bind_mobile
|
||||
///
|
||||
/// 使用微信授权code获取手机号并绑定到当前用户
|
||||
/// ʹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩcode<EFBFBD><EFBFBD>ȡ<EFBFBD>ֻ<EFBFBD><EFBFBD>Ų<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD>û<EFBFBD>
|
||||
/// Requirements: 5.1-5.5
|
||||
/// </remarks>
|
||||
/// <param name="request">绑定手机号请求参数,包含微信授权code</param>
|
||||
/// <returns>绑定成功返回手机号信息,失败返回错误信息</returns>
|
||||
/// <param name="request"><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩcode</param>
|
||||
/// <returns><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ش<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("login_bind_mobile")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ApiResponse<BindMobileResponse>), StatusCodes.Status200OK)]
|
||||
|
|
@ -215,14 +218,14 @@ public class AuthController : ControllerBase
|
|||
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.Code))
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("微信授权code不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩcode<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _authService.WechatBindMobileAsync(userId.Value, request.Code);
|
||||
_logger.LogInformation("WeChat bind mobile successful: UserId={UserId}", userId);
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "绑定成功");
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "<EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
|
@ -232,16 +235,16 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证码绑定手机号
|
||||
/// <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/bindMobile
|
||||
///
|
||||
/// 使用手机号和验证码绑定手机号到当前用户
|
||||
/// ʹ<EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ź<EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ŵ<EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD>û<EFBFBD>
|
||||
/// Requirements: 5.1-5.5
|
||||
/// </remarks>
|
||||
/// <param name="request">绑定手机号请求参数,包含手机号和验证码</param>
|
||||
/// <returns>绑定成功返回手机号信息,失败返回错误信息</returns>
|
||||
/// <param name="request"><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ź<EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD></param>
|
||||
/// <returns><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ش<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("bindMobile")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ApiResponse<BindMobileResponse>), StatusCodes.Status200OK)]
|
||||
|
|
@ -256,24 +259,24 @@ public class AuthController : ControllerBase
|
|||
|
||||
if (request == null)
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("请求参数不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Mobile))
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("手机号不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD>ֻ<EFBFBD><EFBFBD>Ų<EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Code))
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("验证码不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD><EFBFBD>֤<EFBFBD>벻<EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _authService.BindMobileAsync(userId.Value, request.Mobile, request.Code);
|
||||
_logger.LogInformation("Bind mobile successful: UserId={UserId}", userId);
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "绑定成功");
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "<EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
|
@ -283,16 +286,16 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// H5绑定手机号(无需验证码)
|
||||
/// H5<EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ţ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD>룩
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POST /api/login_bind_mobile_h5
|
||||
///
|
||||
/// H5端直接绑定手机号到当前用户,无需短信验证码
|
||||
/// H5<EFBFBD><EFBFBD>ֱ<EFBFBD>Ӱ<EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD>ŵ<EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD>
|
||||
/// Requirements: 13.1
|
||||
/// </remarks>
|
||||
/// <param name="request">绑定手机号请求参数,包含手机号</param>
|
||||
/// <returns>绑定成功返回结果,失败返回错误信息</returns>
|
||||
/// <param name="request"><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <returns><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ؽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ش<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ</returns>
|
||||
[HttpPost("login_bind_mobile_h5")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ApiResponse<BindMobileResponse>), StatusCodes.Status200OK)]
|
||||
|
|
@ -307,19 +310,19 @@ public class AuthController : ControllerBase
|
|||
|
||||
if (request == null)
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("请求参数不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Mobile))
|
||||
{
|
||||
return ApiResponse<BindMobileResponse>.Fail("手机号不能为空");
|
||||
return ApiResponse<BindMobileResponse>.Fail("<EFBFBD>ֻ<EFBFBD><EFBFBD>Ų<EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD>");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _authService.BindMobileH5Async(userId.Value, request.Mobile);
|
||||
_logger.LogInformation("H5 Bind mobile successful: UserId={UserId}", userId);
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "绑定成功");
|
||||
return ApiResponse<BindMobileResponse>.Success(result, "<EFBFBD>ɹ<EFBFBD>");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
|
@ -329,16 +332,16 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录用户登录
|
||||
/// <EFBFBD><EFBFBD>¼<EFBFBD>û<EFBFBD><EFBFBD><EFBFBD>¼
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// GET|POST /api/login_record
|
||||
///
|
||||
/// 记录用户登录信息,包括设备信息和IP地址
|
||||
/// <EFBFBD><EFBFBD>¼<EFBFBD>û<EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>豸<EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD>IP<EFBFBD><EFBFBD>ַ
|
||||
/// Requirements: 6.1-6.4
|
||||
/// </remarks>
|
||||
/// <param name="request">登录记录请求参数,包含设备信息(可选)</param>
|
||||
/// <returns>登录记录结果</returns>
|
||||
/// <param name="request"><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>豸<EFBFBD><EFBFBD>Ϣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><EFBFBD></param>
|
||||
/// <returns><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD></returns>
|
||||
[HttpPost("login_record")]
|
||||
[HttpGet("login_record")]
|
||||
[Authorize]
|
||||
|
|
@ -354,7 +357,7 @@ public class AuthController : ControllerBase
|
|||
|
||||
try
|
||||
{
|
||||
// 获取客户端IP
|
||||
// <EFBFBD><EFBFBD>ȡ<EFBFBD>ͻ<EFBFBD><EFBFBD><EFBFBD>IP
|
||||
var clientIp = GetClientIp();
|
||||
|
||||
var result = await _authService.RecordLoginAsync(
|
||||
|
|
@ -374,7 +377,7 @@ public class AuthController : ControllerBase
|
|||
#region Private Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前登录用户ID
|
||||
/// <EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD>¼<EFBFBD>û<EFBFBD>ID
|
||||
/// </summary>
|
||||
private long? GetCurrentUserId()
|
||||
{
|
||||
|
|
@ -387,15 +390,15 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取客户端IP地址
|
||||
/// <EFBFBD><EFBFBD>ȡ<EFBFBD>ͻ<EFBFBD><EFBFBD><EFBFBD>IP<EFBFBD><EFBFBD>ַ
|
||||
/// </summary>
|
||||
private string GetClientIp()
|
||||
{
|
||||
// 优先从X-Forwarded-For头获取(经过代理的情况)
|
||||
// <EFBFBD><EFBFBD><EFBFBD>ȴ<EFBFBD>X-Forwarded-Forͷ<72><CDB7>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
var forwardedFor = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(forwardedFor))
|
||||
{
|
||||
// X-Forwarded-For可能包含多个IP,取第一个
|
||||
// X-Forwarded-For<EFBFBD><EFBFBD><EFBFBD>ܰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>IP<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD>
|
||||
var ip = forwardedFor.Split(',').FirstOrDefault()?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(ip))
|
||||
{
|
||||
|
|
@ -403,18 +406,18 @@ public class AuthController : ControllerBase
|
|||
}
|
||||
}
|
||||
|
||||
// 从X-Real-IP头获取
|
||||
// <EFBFBD><EFBFBD>X-Real-IPͷ<50><CDB7>ȡ
|
||||
var realIp = HttpContext.Request.Headers["X-Real-IP"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(realIp))
|
||||
{
|
||||
return realIp;
|
||||
}
|
||||
|
||||
// 从连接获取
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӻ<EFBFBD>ȡ
|
||||
var remoteIp = HttpContext.Connection.RemoteIpAddress;
|
||||
if (remoteIp != null)
|
||||
{
|
||||
// 如果是IPv6的本地回环地址,转换为IPv4
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>IPv6<EFBFBD>ı<EFBFBD><EFBFBD>ػػ<EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>ΪIPv4
|
||||
if (remoteIp.IsIPv4MappedToIPv6)
|
||||
{
|
||||
return remoteIp.MapToIPv4().ToString();
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@ public class AuthService : IAuthService
|
|||
|
||||
if (user == null)
|
||||
{
|
||||
// 1.3 <EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>
|
||||
_logger.LogInformation("[AuthService] <EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڣ<EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>...");
|
||||
// 1.3 新用户,创建并绑定上级
|
||||
_logger.LogInformation("[AuthService] 用户不存在,开始创建新用户, pid={Pid}", pid);
|
||||
var createDto = new CreateUserDto
|
||||
{
|
||||
OpenId = openId,
|
||||
|
|
@ -140,18 +140,20 @@ public class AuthService : IAuthService
|
|||
Headimg = await GetDefaultAvatarAsync(openId),
|
||||
Pid = pid ?? 0
|
||||
};
|
||||
_logger.LogInformation("[AuthService] CreateUserDto.Pid={Pid}", createDto.Pid);
|
||||
|
||||
user = await _userService.CreateUserAsync(createDto);
|
||||
_logger.LogInformation("[AuthService] <EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD>: UserId={UserId}, OpenId={OpenId}", user.Id, openId);
|
||||
_logger.LogInformation("[AuthService] 新用户创建成功: UserId={UserId}, ParentUserId={ParentUserId}", user.Id, user.ParentUserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.4 <20>û<EFBFBD><C3BB><EFBFBD><EFBFBD>ڣ<EFBFBD><DAA3><EFBFBD><EFBFBD><EFBFBD>unionid<69><64><EFBFBD><EFBFBD><EFBFBD>֮ǰΪ<C7B0>գ<EFBFBD>
|
||||
// 1.4 已有用户,不绑定上级
|
||||
_logger.LogInformation("[AuthService] 已有用户登录: UserId={UserId}, ParentUserId={ParentUserId}, pid参数={Pid}(忽略)", user.Id, user.ParentUserId, pid);
|
||||
if (string.IsNullOrWhiteSpace(user.UnionId) && !string.IsNullOrWhiteSpace(unionId))
|
||||
{
|
||||
_logger.LogInformation("[AuthService] <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>unionid: UserId={UserId}", user.Id);
|
||||
_logger.LogInformation("[AuthService] 更新用户unionid: UserId={UserId}", user.Id);
|
||||
await _userService.UpdateUserAsync(user.Id, new UpdateUserDto { UnionId = unionId });
|
||||
_logger.LogInformation("[AuthService] unionid<EFBFBD><EFBFBD><EFBFBD>³ɹ<EFBFBD>");
|
||||
_logger.LogInformation("[AuthService] unionid更新成功");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,9 +59,14 @@ public class UserService : BaseService<User, long>, IUserService
|
|||
if (dto == null)
|
||||
throw new ArgumentNullException(nameof(dto));
|
||||
|
||||
_logger.LogInformation("[UserService] 创建用户: OpenId={OpenId}, Pid={Pid}", dto.OpenId, dto.Pid);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ΨһUID
|
||||
var uid = await GenerateUidAsync();
|
||||
|
||||
var parentUserId = dto.Pid > 0 ? (long?)dto.Pid : null;
|
||||
_logger.LogInformation("[UserService] ParentUserId计算结果: Pid={Pid}, ParentUserId={ParentUserId}", dto.Pid, parentUserId);
|
||||
|
||||
var user = new User
|
||||
{
|
||||
OpenId = dto.OpenId ?? string.Empty,
|
||||
|
|
@ -70,7 +75,7 @@ public class UserService : BaseService<User, long>, IUserService
|
|||
Uid = uid,
|
||||
Nickname = dto.Nickname ?? $"User{Random.Shared.Next(1000, 9999)}",
|
||||
Avatar = dto.Headimg ?? string.Empty,
|
||||
ParentUserId = dto.Pid > 0 ? dto.Pid : null,
|
||||
ParentUserId = parentUserId,
|
||||
Status = 1,
|
||||
IsTest = 0,
|
||||
CreateTime = DateTime.Now,
|
||||
|
|
@ -80,7 +85,8 @@ public class UserService : BaseService<User, long>, IUserService
|
|||
await _dbSet.AddAsync(user);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation($"User created: Id={user.Id}, Uid={user.Uid}, OpenId={user.OpenId}");
|
||||
_logger.LogInformation("[UserService] 用户创建成功: Id={Id}, Uid={Uid}, OpenId={OpenId}, ParentUserId={ParentUserId}",
|
||||
user.Id, user.Uid, user.OpenId, user.ParentUserId);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,27 +40,35 @@
|
|||
parseInviterFromLaunch(options) {
|
||||
if (!options) return
|
||||
|
||||
console.log('[App] parseInviterFromLaunch 原始options:', JSON.stringify(options))
|
||||
|
||||
let inviterId = null
|
||||
|
||||
// 1. 扫码进入:解析 scene 参数(inviter={userId})
|
||||
if (options.scene) {
|
||||
const scene = decodeURIComponent(options.scene)
|
||||
console.log('扫码 scene:', scene)
|
||||
console.log('[App] 扫码 scene 解码后:', scene)
|
||||
const match = scene.match(/inviter=(\d+)/)
|
||||
if (match) {
|
||||
inviterId = match[1]
|
||||
console.log('[App] 从scene解析到inviterId:', inviterId)
|
||||
} else {
|
||||
console.log('[App] scene中未匹配到inviter参数')
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 分享链接进入:解析 inviterId 参数
|
||||
if (!inviterId && options.inviterId) {
|
||||
inviterId = options.inviterId
|
||||
console.log('[App] 从query参数获取inviterId:', inviterId)
|
||||
}
|
||||
|
||||
// 存储邀请人ID(仅未登录用户需要绑定)
|
||||
if (inviterId) {
|
||||
console.log('检测到邀请人ID:', inviterId)
|
||||
console.log('[App] 存储inviterId到Storage:', inviterId)
|
||||
uni.setStorageSync('inviterId', inviterId)
|
||||
} else {
|
||||
console.log('[App] 未检测到邀请人参数')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -697,7 +697,10 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: $spacing-lg;
|
||||
margin: -#{$spacing-lg} -#{$spacing-lg} $spacing-lg;
|
||||
padding: $spacing-md $spacing-lg;
|
||||
background-color: #FF4757;
|
||||
border-radius: $border-radius-lg $border-radius-lg 0 0;
|
||||
|
||||
.commission-title {
|
||||
font-size: $font-size-lg;
|
||||
|
|
@ -786,23 +789,25 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
margin: 0 $spacing-lg;
|
||||
background-color: $bg-white;
|
||||
border-radius: $border-radius-lg;
|
||||
padding: $spacing-lg;
|
||||
overflow: hidden;
|
||||
min-height: 300rpx;
|
||||
border: 2rpx solid #FF4D4F;
|
||||
|
||||
.record-header {
|
||||
margin-bottom: $spacing-md;
|
||||
background-color: #FF4D4F;
|
||||
padding: $spacing-md $spacing-lg;
|
||||
|
||||
.record-title {
|
||||
font-size: $font-size-lg;
|
||||
font-weight: $font-weight-bold;
|
||||
color: #FF4D4F;
|
||||
color: $text-white;
|
||||
}
|
||||
}
|
||||
|
||||
.record-table-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacing-sm 0;
|
||||
padding: $spacing-sm $spacing-lg;
|
||||
border-bottom: 1rpx solid $border-light;
|
||||
|
||||
text {
|
||||
|
|
@ -816,7 +821,7 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
.record-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $spacing-md 0;
|
||||
padding: $spacing-md $spacing-lg;
|
||||
border-bottom: 1rpx solid $border-light;
|
||||
|
||||
&:last-child {
|
||||
|
|
@ -914,13 +919,19 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
.rule-popup {
|
||||
.popup-body {
|
||||
max-height: 500rpx;
|
||||
padding: 0 $spacing-lg $spacing-lg;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rule-content {
|
||||
padding-right: $spacing-xs;
|
||||
|
||||
.rule-text {
|
||||
font-size: $font-size-md;
|
||||
color: $text-color;
|
||||
line-height: 1.8;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1086,9 +1097,12 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
|
||||
// 提现记录弹窗
|
||||
.wr-popup {
|
||||
width: 680rpx;
|
||||
|
||||
.wr-body {
|
||||
max-height: 600rpx;
|
||||
padding: 0 $spacing-lg $spacing-lg;
|
||||
padding: 0 $spacing-md $spacing-lg;
|
||||
box-sizing: border-box;
|
||||
|
||||
.wr-table-header {
|
||||
display: flex;
|
||||
|
|
@ -1120,17 +1134,17 @@ onMounted(() => { userStore.restoreFromStorage() })
|
|||
}
|
||||
|
||||
.wr-col-time {
|
||||
width: 40%;
|
||||
flex: 2;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wr-col-amount {
|
||||
width: 30%;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wr-col-status {
|
||||
width: 30%;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,16 +128,22 @@ async function handleGetPhoneNumber(e) {
|
|||
|
||||
// 传递邀请人ID(扫码或分享链接进入时存储的)
|
||||
const inviterId = uni.getStorageSync('inviterId')
|
||||
console.log('[Login] 从Storage读取inviterId:', inviterId)
|
||||
if (inviterId) {
|
||||
loginData.pid = String(inviterId)
|
||||
console.log('[Login] 设置loginData.pid:', loginData.pid)
|
||||
} else {
|
||||
console.log('[Login] 无inviterId,不传pid')
|
||||
}
|
||||
|
||||
console.log('[Login] 发送登录请求, loginData:', JSON.stringify(loginData))
|
||||
const res = await post('/login', loginData, { needAuth: false })
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (res && res.code === 0 && res.data) {
|
||||
// 登录成功,清除邀请人ID缓存
|
||||
console.log('[Login] 登录成功, userId:', res.data.userId)
|
||||
uni.removeStorageSync('inviterId')
|
||||
|
||||
// 保存登录信息(LoginResponse 只有 token 和 userId)
|
||||
|
|
@ -219,16 +225,20 @@ async function handleLogin() {
|
|||
|
||||
// 传递邀请人ID
|
||||
const inviterId = uni.getStorageSync('inviterId')
|
||||
console.log('[Login-backup] 从Storage读取inviterId:', inviterId)
|
||||
if (inviterId) {
|
||||
loginData.pid = String(inviterId)
|
||||
console.log('[Login-backup] 设置loginData.pid:', loginData.pid)
|
||||
}
|
||||
|
||||
console.log('[Login-backup] 发送登录请求, loginData:', JSON.stringify(loginData))
|
||||
const res = await post('/login', loginData, { needAuth: false })
|
||||
|
||||
uni.hideLoading()
|
||||
|
||||
if (res && res.code === 0 && res.data) {
|
||||
// 登录成功,清除邀请人ID缓存
|
||||
console.log('[Login-backup] 登录成功, userId:', res.data.userId)
|
||||
uni.removeStorageSync('inviterId')
|
||||
|
||||
// 保存登录信息(LoginResponse 只有 token 和 userId)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user