Files
wangjia 40760aa884
ci / server (push) Failing after 14s
ci / design-tokens (push) Failing after 11s
dudu MVP:五端语音输入法初始提交
- server:Go 网关(WS 流式识别中继/计费配额/微信登录支付 mock/反馈/埋点),gummy provider 已真实联调
- desktop:Tauri 2(全局快捷键 push-to-talk/浮层/托盘/设置/登录购买/反馈/首启引导)
- android:Compose 主 App + IME(键盘内录音直传)
- ios:App + 键盘扩展(1A spike 实证键盘内不可录音,走 deep link 听写)
- design/design-pipeline:设计系统 + token 导出 iOS/Android 主题
- doc:前后端设计文档(HTML);web:官网宣传页;todo:任务看板

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 00:38:37 +08:00

104 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package auth
import (
"context"
"errors"
"strings"
"github.com/google/uuid"
"gorm.io/gorm"
"dudu/server/internal/store"
)
// WechatIdentityInfo 微信授权换取的身份。
type WechatIdentityInfo struct {
OpenID string
UnionID string
Nickname string
Avatar string
}
// WechatClient 微信 OAuth 客户端抽象:真实实现走开放平台 API(2A 凭证就绪后接入),
// 开发期用 MockWechat。
type WechatClient interface {
// ExchangeCode code 换身份;appType: web | mobile
ExchangeCode(ctx context.Context, code, appType string) (WechatIdentityInfo, error)
}
// MockWechat 开发期 mockcode 直接映射 openid(同 code 幂等同一用户),
// unionid = openid 加前缀,昵称取 code 前 8 字符。
type MockWechat struct{}
func (MockWechat) ExchangeCode(_ context.Context, code, _ string) (WechatIdentityInfo, error) {
if code == "" {
return WechatIdentityInfo{}, errors.New("empty code")
}
nick := code
if len(nick) > 8 {
nick = nick[:8]
}
return WechatIdentityInfo{
OpenID: "mock-open-" + code,
UnionID: "mock-union-" + code,
Nickname: nick,
}, nil
}
// MaskNickname 昵称脱敏:"wangjia" → "wang***";中文取首字 + ***。
func MaskNickname(n string) string {
r := []rune(strings.TrimSpace(n))
if len(r) == 0 {
return "用户***"
}
keep := 4
if r[0] > 0x2E80 { // CJK 起始附近,中文名只保留首字
keep = 1
}
if len(r) < keep {
keep = len(r)
}
return string(r[:keep]) + "***"
}
// FindOrCreateUser unionid 优先关联(同一用户多端 openid 归一),其次 openid。
func FindOrCreateUser(db *gorm.DB, info WechatIdentityInfo, appType string) (*store.User, error) {
var ident store.WechatIdentity
err := db.Where("open_id = ?", info.OpenID).First(&ident).Error
if err == nil {
var u store.User
return &u, db.First(&u, "id = ?", ident.UserID).Error
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
var user store.User
err = db.Transaction(func(tx *gorm.DB) error {
// unionid 已存在 → 关联到既有用户
if info.UnionID != "" {
var other store.WechatIdentity
if err := tx.Where("union_id = ?", info.UnionID).First(&other).Error; err == nil {
if err := tx.First(&user, "id = ?", other.UserID).Error; err != nil {
return err
}
return tx.Create(&store.WechatIdentity{
UserID: user.ID, OpenID: info.OpenID, UnionID: info.UnionID, AppType: appType,
}).Error
}
}
// 新用户
user = store.User{
ID: strings.ReplaceAll(uuid.NewString(), "-", "")[:24],
Nickname: info.Nickname, AvatarURL: info.Avatar,
}
if err := tx.Create(&user).Error; err != nil {
return err
}
return tx.Create(&store.WechatIdentity{
UserID: user.ID, OpenID: info.OpenID, UnionID: info.UnionID, AppType: appType,
}).Error
})
return &user, err
}