feat: 完善登录流程、UI适配及协议文档更新

本次提交包含以下修改:
1. 新增微信登录相关服务及页面逻辑,实现首次微信绑定手机号流程
2. 优化产品匹配卡片 UI,移除不必要状态显示
3. 调整首页 scroll-view 属性及 banner 背景样式
4. 完善积分规则校验逻辑,增加时区及策略格式验证
5. 更新协议草案文档,补充资料清单及功能说明
6. 修复分析结果路由路径,适配新的匹配结果页面
7. 调整品牌 Logo 安卓端字体对齐样式
This commit is contained in:
hujun
2026-10-22 11:24:15 +08:00
parent e5406c23a6
commit 2c9d3feab5
36 changed files with 1246 additions and 212 deletions
+12 -3
View File
@@ -7,7 +7,7 @@ export async function requestSmsCode(phone: string): Promise<boolean> {
return true
}
function acceptSession(result: UTSJSONObject, phone: string): string {
export function acceptSession(result: UTSJSONObject, phone: string): string {
const token = result['token'] as string | null
const userId = result['userId'] as string | null
const expiresAt = result['expiresAt'] as string | null
@@ -19,11 +19,20 @@ function acceptSession(result: UTSJSONObject, phone: string): string {
}
export async function loginWithSms(phone: string, code: string): Promise<string> {
return acceptSession(await requestJson('/auth/sms-login', 'POST', { phone, code }), phone)
return acceptSession(await requestSmsLogin(phone, code), phone)
}
export async function loginWithPassword(phone: string, password: string): Promise<string> {
return acceptSession(await requestJson('/auth/password-login', 'POST', { phone, password }), phone)
return acceptSession(await requestPasswordLogin(phone, password), phone)
}
// 页面先校验自身代次与原会话,再接受认证结果;迟到响应不能切换当前账户。
export async function requestSmsLogin(phone: string, code: string): Promise<UTSJSONObject> {
return await requestJson('/auth/sms-login', 'POST', { phone, code })
}
export async function requestPasswordLogin(phone: string, password: string): Promise<UTSJSONObject> {
return await requestJson('/auth/password-login', 'POST', { phone, password })
}
export async function logoutAccount(): Promise<void> {
@@ -0,0 +1,90 @@
import { requestJson } from '@/services/api.uts'
import { acceptSession } from './auth.uts'
export function wechatLoginSupported(): boolean {
// App微信SDK及各平台应用凭据接入后再开放原生入口。
// #ifdef MP-WEIXIN
return true
// #endif
// #ifndef MP-WEIXIN
return false
// #endif
}
export function requestWechatAuthorization(): Promise<string> {
// #ifdef MP-WEIXIN
return new Promise<string>((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (result) => {
const code = result.code
if (code.length == 0) { reject(new Error('未能取得微信登录凭证,请重新操作')); return }
resolve(code)
},
fail: (error) => {
const cancelled = error.errMsg.toLowerCase().includes('cancel') || error.errCode == 1310509
reject(new Error(cancelled ? '已取消微信登录' : '微信授权失败,请重新操作'))
}
})
})
// #endif
// #ifndef MP-WEIXIN
return new Promise<string>((resolve, reject) => {
reject(new Error('当前平台暂未开放微信登录,请使用手机号登录'))
})
// #endif
}
function validTicket(value: string): boolean { return /^[A-Za-z0-9_-]{32,128}$/.test(value) }
function validPhone(value: string): boolean { return /^1[3-9][0-9]{9}$/.test(value) }
function validateResult(result: UTSJSONObject): UTSJSONObject {
const status = result.getString('status') ?? ''
if (status == 'bind_required') {
const expires = result.getNumber('expiresIn') ?? 0
if (!validTicket(result.getString('bindingTicket') ?? '') || expires < 1 || expires > 600 || Math.floor(expires) != expires || result['token'] != null)
throw new Error('微信绑定响应格式不完整,请重新微信登录')
} else if (status == 'authenticated') {
const expiry = Date.parse(result.getString('expiresAt') ?? '')
if (result.getBoolean('consentsConfirmed') != true || !validPhone(result.getString('phone') ?? '')
|| !/^[1-9][0-9]{0,18}$/.test(result.getString('userId') ?? '') || (result.getString('token') ?? '').length == 0
|| isNaN(expiry) || expiry <= Date.now()) throw new Error('微信登录响应缺少有效账户或协议确认')
} else throw new Error('微信登录响应状态无效')
return result
}
export async function requestWechatLogin(code: string, agreements: UTSJSONObject[]): Promise<UTSJSONObject> {
if (code.length == 0 || code.length > 256 || agreements.length != 2) throw new Error('请重新确认协议并微信登录')
let terms = false; let privacy = false; let previousRequestId = ''
for (const item of agreements) {
const agreementCode = item['agreementCode'] as string | null ?? ''
const requestId = item['requestId'] as string | null ?? ''
if ((agreementCode == 'terms' && !terms)) terms = true
else if ((agreementCode == 'privacy' && !privacy)) privacy = true
else throw new Error('用户协议和隐私政策必须分别确认')
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(item['agreementVersion'] as string | null ?? '')
|| !/^[a-f0-9]{64}$/.test(item['contentHash'] as string | null ?? '') || !/^[A-Za-z0-9_-]{16,64}$/.test(requestId)
|| requestId == previousRequestId) throw new Error('协议确认信息不完整,请重新阅读并确认')
previousRequestId = requestId
}
return validateResult(await requestJson('/auth/wechat-login', 'POST', { platform: 'mp-weixin', code, agreements }))
}
export async function requestWechatBindingCode(bindingTicket: string, phone: string): Promise<boolean> {
if (!validTicket(bindingTicket) || !validPhone(phone)) throw new Error('请重新微信登录并输入正确手机号')
await requestJson('/auth/wechat/sms-code', 'POST', { bindingTicket, phone })
return true
}
export async function bindWechatPhone(bindingTicket: string, phone: string, code: string): Promise<UTSJSONObject> {
if (!validTicket(bindingTicket) || !validPhone(phone) || !/^[0-9]{6}$/.test(code)) throw new Error('请核对手机号和6位验证码')
const result = validateResult(await requestJson('/auth/wechat/bind-phone', 'POST', { bindingTicket, phone, code }))
if (result.getString('status') != 'authenticated') throw new Error('手机号绑定尚未完成,请重新微信登录')
return result
}
export function acceptWechatSession(result: UTSJSONObject): string {
validateResult(result)
if (result.getString('status') != 'authenticated') throw new Error('请先完成手机号绑定')
return acceptSession(result, result.getString('phone') ?? '')
}