fix(weixin): 扫码登录二维码真实可扫——前端生成QR(修坏图) + DecodeConfig容忍空config
weixin qrcode_img_content 是微信liteapp HTML页(非图片),后端fetch转base64只得<!doctype>→前端img坏图。
- 后端 login-qr 改返回 qrUrl(qrcode_img_content URL)+qrCode(原始qrcode),去掉坏 qrImage
- 前端用 qrcode 库从 qrUrl 生成真实二维码图片(微信扫https链接即开连接页), 新增 qrcode+@types/qrcode(纯UI依赖)
- DecodeConfig: nil config map 当空map解码(不报错), 否则 pending账号(空config)构建被跳过→未注册→登录端点404
- 测试更新(DecodeConfigNil/admin QR端点断言 qrUrl+qrCode)
- 另有 weixinAccountFor 裸weixin兜底(此前提交 d3b0921) 保证 accountId 命中
- 实测: login-qr 200{waiting, qrUrl=89}, 前端能看到可扫二维码
build/vet/test 全绿, 已部署.
This commit is contained in:
1 parent
d3b0921491
commit
36bd19e997
8 files changed
+394
-29
No files matched your search
@@ -83,8 +83,11 @@ var (
|
||||
// configuration as strongly typed structs. Unknown keys are ignored; missing
|
||||
// keys leave the corresponding zero value.
|
||||
func DecodeConfig(raw map[string]any, v any) error {
|
||||
// A nil config map is treated as empty (decodes to the zero value), so a
|
||||
// pending account with no keys yet (e.g. a weixin-ilink scan-login account the
|
||||
// panel created before binding) builds with defaults rather than erroring.
|
||||
if raw == nil {
|
||||
return errors.New("adapter: nil config map")
|
||||
raw = map[string]any{}
|
||||
}
|
||||
b, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
|
||||
@@ -100,8 +100,13 @@ func TestDecodeConfig(t *testing.T) {
|
||||
func TestDecodeConfigNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// A nil config map is treated as empty (decodes to zero value), so a pending
|
||||
// account with no keys yet builds with defaults rather than erroring.
|
||||
var c struct{ Token string }
|
||||
if err := DecodeConfig(nil, &c); err == nil {
|
||||
t.Error("DecodeConfig(nil, ...) error = nil, want non-nil")
|
||||
if err := DecodeConfig(nil, &c); err != nil {
|
||||
t.Fatalf("DecodeConfig(nil, ...) error = %v, want nil (empty config)", err)
|
||||
}
|
||||
if c.Token != "" {
|
||||
t.Fatalf("Token = %q, want empty zero value", c.Token)
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,12 @@ type weixinLoginAdmin interface {
|
||||
}
|
||||
|
||||
// weixinLoginQRResponse is the wire shape of GET /admin/channels/{platform}/login-qr.
|
||||
// qrUrl is the WeChat QR image URL (qrcode_img_content) and qrCode is the raw scan
|
||||
// identifier (qrcode); the frontend renders a scannable QR from qrUrl, since the
|
||||
// liteapp URL is an HTML page that cannot be fetched as an image.
|
||||
type weixinLoginQRResponse struct {
|
||||
QrUrl string `json:"qrUrl,omitempty"`
|
||||
QrImage string `json:"qrImage,omitempty"`
|
||||
QrCode string `json:"qrCode,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
@@ -77,9 +80,9 @@ func (s *Server) adminWeixinLoginQR(w http.ResponseWriter, r *http.Request) {
|
||||
status, _ := wx.LoginStatus(r.Context())
|
||||
s.auditClaims(r, "channel.weixin.login.qr", "ok")
|
||||
writeJSON(w, http.StatusOK, weixinLoginQRResponse{
|
||||
QrUrl: qr.Image,
|
||||
QrImage: qrImageDataURL(r.Context(), qr.Image),
|
||||
Status: status,
|
||||
QrUrl: qr.Image,
|
||||
QrCode: qr.Qrcode,
|
||||
Status: status,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -196,19 +196,19 @@ func TestAdminWeixinLoginQR_ReturnsQRAndStatus(t *testing.T) {
|
||||
}
|
||||
var out struct {
|
||||
Data struct {
|
||||
QrUrl string `json:"qrUrl"`
|
||||
QrImage string `json:"qrImage"`
|
||||
Status string `json:"status"`
|
||||
QrUrl string `json:"qrUrl"`
|
||||
QrCode string `json:"qrCode"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
t.Fatalf("decode login-qr: %v", err)
|
||||
}
|
||||
if out.Data.QrUrl == "" {
|
||||
t.Errorf("qrUrl empty, want the QR image content")
|
||||
t.Errorf("qrUrl empty, want the QR image URL (qrcode_img_content)")
|
||||
}
|
||||
if !strings.HasPrefix(out.Data.QrImage, "data:image/png;base64,") {
|
||||
t.Errorf("qrImage = %q, want a base64 data URL", out.Data.QrImage)
|
||||
if out.Data.QrCode == "" {
|
||||
t.Errorf("qrCode empty, want the raw scan identifier")
|
||||
}
|
||||
if out.Data.Status != weixin_ilink.LoginStatusWaiting && out.Data.Status != weixin_ilink.LoginStatusConfirmed {
|
||||
t.Errorf("status = %q, want waiting|confirmed", out.Data.Status)
|
||||
|
||||
Reference in new issue
Block a user