v0.2.6 - 移除注册时的手机号和短信验证功能(C环境)

This commit is contained in:
甲辰生产 2026-06-13 11:15:12 +08:00
parent 645819844e
commit 02711d73c9
11 changed files with 45 additions and 234 deletions

View File

@ -1 +1 @@
1.2.98 VERSION=v0.2.1

View File

@ -91,22 +91,8 @@ def register(user_data: UserCreate, db: Session = Depends(get_db)):
# 更新: # 更新:
# 更新: # 更新:
# 检查手机号是否已存在 # 检查手机号是否已存在移除2026-06-13
# 更新: # 更新:
if user_data.phone:
# 更新:
existing_phone = db.query(User).filter(User.phone == user_data.phone).first()
# 更新:
if existing_phone:
# 更新:
raise HTTPException(
# 更新:
status_code=status.HTTP_400_BAD_REQUEST,
# 更新:
detail="E00040:该手机号已被注册,请更换手机号"
# 更新:
)
# 更新:
if user_data.email: if user_data.email:
# 更新: # 更新:
existing_email = db.query(User).filter(User.email == user_data.email).first() existing_email = db.query(User).filter(User.email == user_data.email).first()

View File

@ -109,7 +109,7 @@ def get_information_list(
user_id: Optional[str] = Query(None, description="用户ID用于获取该用户的行情"), user_id: Optional[str] = Query(None, description="用户ID用于获取该用户的行情"),
deal_date: Optional[str] = Query(None, description="成交日期过滤格式YYYY-MM-DD"), deal_date: Optional[str] = Query(None, description="成交日期过滤格式YYYY-MM-DD"),
page: int = Query(1, ge=1), page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=500), page_size: int = Query(20, ge=1, le=1000),
current_user: Optional[User] = Depends(get_current_user), current_user: Optional[User] = Depends(get_current_user),
db: Session = Depends(get_db), db: Session = Depends(get_db),
response: Response = None response: Response = None
@ -880,7 +880,7 @@ def get_deal_stats(
@router.get("/my/list", response_model=List[InformationResponse]) @router.get("/my/list", response_model=List[InformationResponse])
def get_my_information_list( def get_my_information_list(
page: int = Query(1, ge=1), page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=500), page_size: int = Query(20, ge=1, le=1000),
current_user: User = Depends(get_current_user), current_user: User = Depends(get_current_user),
db: Session = Depends(get_db) db: Session = Depends(get_db)
): ):

View File

@ -20,6 +20,7 @@ class UserBase(BaseModel):
class UserCreate(UserBase): class UserCreate(UserBase):
password: str = Field(..., min_length=6) password: str = Field(..., min_length=6)
invite_code: Optional[str] = Field(None, alias="inviteCode") # 填写的邀请码(选填) invite_code: Optional[str] = Field(None, alias="inviteCode") # 填写的邀请码(选填)
# 移除手机号和短信验证2026-06-13
class UserUpdate(BaseModel): class UserUpdate(BaseModel):

View File

@ -1,5 +1,18 @@
# 版本更新记录 # 版本更新记录
## v0.2.5 (2026-05-13)
### 前端更新
- **News.jsx**
- 移除成交行情价格统计表格,只显示筛选后的结果
- 成交行情获取数量改为500条
- **YichensBoard.jsx**
- 增加复制链接功能,点击可复制原帖链接
### 环境配置
- C环境Nginx配置为代理到B环境后端
## v0.2.3 (2026-05-11) ## v0.2.3 (2026-05-11)
### 前端更新 ### 前端更新

View File

@ -1 +1 @@
VERSION=v0.1.8 VERSION=0.2.5

View File

@ -1,17 +1,20 @@
{ {
"version": "0.2.4", "version": "0.2.6",
"updated": "2026-05-11", "updated": "2026-06-13",
"modules": { "modules": {
"frontend": { "frontend": {
"version": "0.2.0", "version": "0.2.2",
"pages": { "pages": {
"Login": "0.0.2",
"Add": "0.0.2", "Add": "0.0.2",
"News": "0.2.0" "News": "0.1.2",
"YichensBoard": "0.0.5"
} }
}, },
"backend": { "backend": {
"version": "0.2.1", "version": "0.2.2",
"routers": { "routers": {
"auth": "0.0.2",
"seek": "0.2.0", "seek": "0.2.0",
"purchase": "0.3.0" "purchase": "0.3.0"
}, },

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<title>甲辰收藏 v=0.0.9</title> <title>甲辰收藏 v0.2.5</title>
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="/static/icons/favicon.png" /> <link rel="icon" type="image/png" href="/static/icons/favicon.png" />

View File

@ -1,7 +1,7 @@
/** /**
* Login - 登录页面 * Login - 登录页面
* Version: 0.0.1 * Version: 0.0.2 (2026-06-13)
* 更新 * 更新移除注册时的手机号和短信验证功能
*/ */
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
@ -23,79 +23,22 @@ export default function Login() {
password: '', password: '',
confirmPassword: '', confirmPassword: '',
email: '', email: '',
phone: '',
verifyCode: '',
inviteCode: '' inviteCode: ''
}) })
const [registerLoading, setRegisterLoading] = useState(false) const [registerLoading, setRegisterLoading] = useState(false)
const [registerError, setRegisterError] = useState('') const [registerError, setRegisterError] = useState('')
//
const [sendingCode, setSendingCode] = useState(false)
const [codeCountdown, setCodeCountdown] = useState(0)
const [codeSent, setCodeSent] = useState(false)
// //
useEffect(() => { useEffect(() => {
generateCaptcha() generateCaptcha()
}, []) }, [])
//
useEffect(() => {
if (codeCountdown > 0) {
const timer = setTimeout(() => setCodeCountdown(codeCountdown - 1), 1000)
return () => clearTimeout(timer)
}
}, [codeCountdown])
const generateCaptcha = () => { const generateCaptcha = () => {
const num1 = Math.floor(Math.random() * 10) const num1 = Math.floor(Math.random() * 10)
const num2 = Math.floor(Math.random() * 10) const num2 = Math.floor(Math.random() * 10)
setCaptcha({ num1, num2, answer: '' }) setCaptcha({ num1, num2, answer: '' })
} }
//
const handleSendCode = async () => {
if (!registerData.phone) {
setRegisterError('请先输入手机号')
return
}
//
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(registerData.phone)) {
setRegisterError('请输入正确的手机号')
return
}
setSendingCode(true)
setRegisterError('')
try {
const res = await fetch('/api/auth/send-verification-code', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ phone: registerData.phone })
})
const data = await res.json()
if (data.success) {
setCodeSent(true)
setCodeCountdown(60)
setRegisterError('')
} else {
setRegisterError(data.message || '发送失败')
}
} catch (err) {
setRegisterError('发送失败,请稍后重试')
} finally {
setSendingCode(false)
}
}
// //
const handleRegister = async () => { const handleRegister = async () => {
// //
@ -124,33 +67,13 @@ export default function Login() {
return return
} }
//
if (!registerData.phone) {
setRegisterError('手机号为必填项')
return
}
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(registerData.phone)) {
setRegisterError('请输入正确的手机号')
return
}
//
if (!registerData.verifyCode) {
setRegisterError('请输入短信获取')
return
}
setRegisterLoading(true) setRegisterLoading(true)
setRegisterError('') setRegisterError('')
try { try {
const payload = { const payload = {
username: registerData.username, username: registerData.username,
password: registerData.password, password: registerData.password
phone: registerData.phone,
verifyCode: registerData.verifyCode
} }
if (registerData.email) { if (registerData.email) {
payload.email = registerData.email payload.email = registerData.email
@ -175,9 +98,7 @@ export default function Login() {
alert('注册成功!请登录') alert('注册成功!请登录')
setShowRegister(false) setShowRegister(false)
setRegisterData({ username: '', password: '', confirmPassword: '', email: '', phone: '', verifyCode: '', inviteCode: '' }) setRegisterData({ username: '', password: '', confirmPassword: '', email: '', inviteCode: '' })
setCodeSent(false)
setCodeCountdown(0)
generateCaptcha() generateCaptcha()
} catch (err) { } catch (err) {
console.error('注册错误:', err) console.error('注册错误:', err)
@ -491,68 +412,6 @@ export default function Login() {
/> />
</div> </div>
{/* 手机号 */}
<div style={{ marginBottom: '16px' }}>
<input
type="tel"
value={registerData.phone}
onChange={(e) => setRegisterData(prev => ({ ...prev, phone: e.target.value }))}
placeholder="手机号11位"
maxLength={11}
style={{
width: '100%',
padding: '14px',
borderRadius: '8px',
border: '1px solid rgba(255,255,255,0.2)',
background: 'rgba(255,255,255,0.05)',
color: '#fff',
fontSize: '16px',
outline: 'none'
}}
/>
</div>
{/* 短信获取 */}
<div style={{ marginBottom: '16px' }}>
<div style={{ display: 'flex', gap: '12px' }}>
<input
type="text"
value={registerData.verifyCode}
onChange={(e) => setRegisterData(prev => ({ ...prev, verifyCode: e.target.value }))}
placeholder="短信验证码"
maxLength={6}
style={{
flex: 1, minWidth: "90px", width: "auto",
padding: '14px',
borderRadius: '8px',
border: '1px solid rgba(255,255,255,0.2)',
background: 'rgba(255,255,255,0.05)',
color: '#fff',
fontSize: '16px',
outline: 'none'
}}
/>
<button
onClick={handleSendCode}
disabled={sendingCode || codeCountdown > 0}
style={{
minWidth: "90px", width: "auto",
padding: '14px',
borderRadius: '8px',
border: 'none',
background: codeCountdown > 0 ? 'rgba(148, 163, 184, 0.3)' : 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',
color: '#fff',
fontSize: '14px',
fontWeight: '600',
cursor: codeCountdown > 0 ? 'not-allowed' : 'pointer',
whiteSpace: 'nowrap'
}}
>
{codeCountdown > 0 ? `${codeCountdown}` : sendingCode ? '发送中...' : '获取验证码'}
</button>
</div>
</div>
{/* 邮箱(可选) */} {/* 邮箱(可选) */}
<div style={{ marginBottom: '16px' }}> <div style={{ marginBottom: '16px' }}>
<input <input
@ -656,10 +515,8 @@ export default function Login() {
<button <button
onClick={() => { onClick={() => {
setShowRegister(false) setShowRegister(false)
setRegisterData({ username: '', password: '', confirmPassword: '', email: '', phone: '', verifyCode: '', inviteCode: '' }) setRegisterData({ username: '', password: '', confirmPassword: '', email: '', inviteCode: '' })
setRegisterError('') setRegisterError('')
setCodeSent(false)
setCodeCountdown(0)
}} }}
style={{ style={{
flex: 1, minWidth: "90px", width: "auto", flex: 1, minWidth: "90px", width: "auto",

View File

@ -1,7 +1,7 @@
/** /**
* News - 资讯列表页面 * News - 资讯列表页面
* Version: 0.1.0 (2026-05-07) * Version: 0.1.2 (2026-05-13)
* 更新成交行情列表和详情弹窗列表都按成交日期(deal_date)倒序排序 * 更新移除成交行情价格统计表格增加获取数量到500
*/ */
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
@ -97,7 +97,7 @@ export default function News() {
? `${API_BASE}/api/seek/list` ? `${API_BASE}/api/seek/list`
: `${API_BASE}/api/deal/list` : `${API_BASE}/api/deal/list`
// 500 //
if (activeTab === 'deal') { if (activeTab === 'deal') {
url += (url.includes('?') ? '&' : '?') + 'page_size=500' url += (url.includes('?') ? '&' : '?') + 'page_size=500'
if (dealDate) { if (dealDate) {
@ -660,60 +660,6 @@ export default function News() {
</button> </button>
)} )}
</div> </div>
<div style={{ overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '13px' }}>
<thead>
<tr>
<th style={{ padding: '8px', textAlign: 'left', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}></th>
{packagings.map(p => (
<th key={p} style={{ padding: '8px', textAlign: 'center', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>{p}</th>
))}
</tr>
</thead>
<tbody>
{(() => {
//
const catAvg = categories.map(cat => {
const prices = []
packagings.forEach(pkg => {
const d = calcAvg(pkg, cat)
if (d) prices.push(d.avg)
})
const avg = prices.length > 0 ? Math.round(prices.reduce((a,b) => a+b, 0) / prices.length) : 0
return { cat, avg }
}).filter(c => c.avg > 0)
//
catAvg.sort((a, b) => a.avg - b.avg)
const categoryOrder = ['带4号', '带7号', '永恒号', '天马号', '天马王', '金山号', '金山王', '钻石号', '朦胧号', '朦胧王', '金马号', '金马王', '倒置号', '圆圆号']
const sortedCats = categoryOrder.filter(cat => catAvg.some(c => c.cat === cat))
.concat(catAvg.filter(c => !categoryOrder.includes(c.cat)).map(c => c.cat))
return sortedCats.map(cat => {
const rowData = packagings.map(pkg => calcAvg(pkg, cat))
const hasData = rowData.some(d => d !== null)
if (!hasData) return null
return (
<tr key={cat}>
<td style={{ padding: '8px', color: '#fbbf24', fontWeight: '500', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>{cat}</td>
{rowData.map((d, i) => (
<td key={i} style={{ padding: '8px', textAlign: 'center', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
{d ? (
<div style={{ color: '#22c55e', fontWeight: '600', cursor: 'pointer' }}
onClick={() => setDealDetailItems(d.items)}>
¥{d.avg.toLocaleString()}
<span style={{ color: '#64748b', fontSize: '11px', marginLeft: '4px' }}>({d.count})</span>
</div>
) : <span style={{ color: '#475569' }}>-</span>}
</td>
))}
</tr>
)
})
})()}
</tbody>
</table>
</div>
</div> </div>
) )
})()} })()}

View File

@ -1,7 +1,7 @@
/** /**
* YichensBoard - 一尘看板页面 * YichensBoard - 一尘看板页面
* Version: 0.0.3 * Version: 0.0.5 (2026-05-13)
* 更新 * 更新增加复制链接功能
*/ */
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
@ -265,7 +265,12 @@ export default function YichensBoard() {
<div style={{ color: '#e2e8f0', fontSize: 13, lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 300, overflowY: 'auto' }}> <div style={{ color: '#e2e8f0', fontSize: 13, lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word', maxHeight: 300, overflowY: 'auto' }}>
{post.content} {post.content}
</div> </div>
{post.url && <a href={post.url} target='_blank' rel='noopener noreferrer' style={{ display:'inline-block', marginTop:12, padding:'8px 16px', background:'rgba(59,130,246,0.2)', borderRadius:8, color:'#60a5fa', textDecoration:'none', fontSize:13 }}>查看原帖</a>} {post.url && (
<div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
<a href={post.url} target='_blank' rel='noopener noreferrer' style={{ padding:'8px 16px', background:'rgba(59,130,246,0.2)', borderRadius:8, color:'#60a5fa', textDecoration:'none', fontSize:13 }}>查看原帖</a>
<button onClick={() => { navigator.clipboard.writeText(post.url); alert('链接已复制!'); }} style={{ padding:'8px 16px', background:'rgba(34,197,94,0.2)', borderRadius:8, color:'#22c55e', border:'none', cursor:'pointer', fontSize:13 }}>复制链接</button>
</div>
)}
</div> </div>
)} )}
</div> </div>