package store import ( "context" "database/sql" "errors" ) type MemberOverview struct { TenantID int64 `db:"tenant_id"` MemberID int64 `db:"member_id"` MemberNo string `db:"member_no"` Username string `db:"username"` Mobile string `db:"mobile"` InviteCode string `db:"invite_code"` MemberStatus int `db:"member_status"` MemberCategory int `db:"member_category"` CreditScore int `db:"credit_score"` AvatarAttachmentID int64 `db:"avatar_attachment_id"` AvatarURL string `db:"avatar_url"` LevelID int64 `db:"level_id"` LevelCode string `db:"level_code"` LevelName string `db:"level_name"` LevelIconURL string `db:"level_icon_url"` CommissionRate int `db:"commission_rate"` AvailableAmount int64 `db:"available_amount"` FrozenAmount int64 `db:"frozen_amount"` TotalIncome int64 `db:"total_income"` TotalExpense int64 `db:"total_expense"` Currency string `db:"currency"` WalletStatus int `db:"wallet_status"` WalletVersion int `db:"wallet_version"` } type AccountSecurity struct { PasswordHash string `db:"password_hash"` TradePasswordHash string `db:"trade_password_hash"` MemberStatus int `db:"member_status"` } type Avatar struct { AttachmentID int64 `db:"attachment_id"` URL string `db:"url"` } func (s *Store) MemberOverview(ctx context.Context, tenantID, memberID int64) (*MemberOverview, error) { if s == nil || s.db == nil { return nil, errors.New("store is unavailable") } var row MemberOverview err := s.db.GetContext(ctx, &row, `SELECT m.tenant_id,m.id AS member_id,m.member_no,m.username,m.mobile, m.invite_code,m.member_status,m.risk_status AS member_category,m.credit_score, COALESCE(p.avatar_attachment_id,0) AS avatar_attachment_id,COALESCE(a.file_url,'') AS avatar_url, COALESCE(l.id,0) AS level_id,COALESCE(l.level_code,'') AS level_code, COALESCE(l.name,'') AS level_name,COALESCE(l.icon_url,'') AS level_icon_url, COALESCE(lv.commission_rate,0) AS commission_rate, COALESCE(w.available_amount,0) AS available_amount,COALESCE(w.frozen_amount,0) AS frozen_amount, COALESCE(w.total_income,0) AS total_income,COALESCE(w.total_expense,0) AS total_expense, COALESCE(w.currency,'CNY') AS currency,COALESCE(w.account_status,1) AS wallet_status, COALESCE(w.version,0) AS wallet_version FROM hg_app_member m LEFT JOIN hg_app_member_profile p ON p.tenant_id=m.tenant_id AND p.member_id=m.id LEFT JOIN hg_sys_attachment a ON a.id=p.avatar_attachment_id AND a.status=1 LEFT JOIN hg_app_member_level l ON l.tenant_id=m.tenant_id AND l.id=m.level_id AND l.deleted_at IS NULL LEFT JOIN hg_app_member_level_version lv ON lv.tenant_id=l.tenant_id AND lv.id=l.current_version_id LEFT JOIN hg_wallet_account w ON w.tenant_id=m.tenant_id AND w.member_id=m.id AND w.currency='CNY' WHERE m.tenant_id=? AND m.id=? AND m.member_status=1 AND m.deleted_at IS NULL LIMIT 1`, tenantID, memberID) if errors.Is(err, sql.ErrNoRows) { return nil, ErrAccountNotFound } return &row, err } func (s *Store) UpdateAvatar(ctx context.Context, tenantID, memberID, attachmentID int64) (*Avatar, error) { if s == nil || s.db == nil { return nil, errors.New("store is unavailable") } tx, err := s.db.BeginTxx(ctx, nil) if err != nil { return nil, err } defer tx.Rollback() var activeMemberID int64 err = tx.GetContext(ctx, &activeMemberID, `SELECT id FROM hg_app_member WHERE tenant_id=? AND id=? AND member_status=1 AND deleted_at IS NULL LIMIT 1 FOR UPDATE`, tenantID, memberID) if errors.Is(err, sql.ErrNoRows) { return nil, ErrAccountNotFound } if err != nil { return nil, err } var avatar Avatar err = tx.GetContext(ctx, &avatar, `SELECT id AS attachment_id,COALESCE(file_url,'') AS url FROM hg_sys_attachment WHERE id=? AND app_id='workbench' AND member_id=? AND status=1 AND mime_type LIKE 'image/%' LIMIT 1 FOR UPDATE`, attachmentID, memberID) if errors.Is(err, sql.ErrNoRows) { return nil, ErrAttachmentInvalid } if err != nil { return nil, err } _, err = tx.ExecContext(ctx, `INSERT INTO hg_app_member_profile (tenant_id,member_id,avatar_attachment_id,created_by,updated_by) VALUES(?,?,?,0,0) ON DUPLICATE KEY UPDATE avatar_attachment_id=VALUES(avatar_attachment_id),updated_at=CURRENT_TIMESTAMP(3)`, tenantID, memberID, attachmentID) if err != nil { return nil, err } if err := tx.Commit(); err != nil { return nil, err } return &avatar, nil } func (s *Store) AccountSecurity(ctx context.Context, tenantID, memberID int64) (*AccountSecurity, error) { if s == nil || s.db == nil { return nil, errors.New("store is unavailable") } var row AccountSecurity err := s.db.GetContext(ctx, &row, `SELECT password_hash,trade_password_hash,member_status FROM hg_app_member WHERE tenant_id=? AND id=? AND member_status=1 AND deleted_at IS NULL LIMIT 1`, tenantID, memberID) if errors.Is(err, sql.ErrNoRows) { return nil, ErrAccountNotFound } return &row, err } func (s *Store) UpdateLoginPassword(ctx context.Context, tenantID, memberID int64, oldHash, newHash string) error { result, err := s.db.ExecContext(ctx, `UPDATE hg_app_member SET password_hash=?,updated_at=CURRENT_TIMESTAMP(3) WHERE tenant_id=? AND id=? AND password_hash=? AND member_status=1 AND deleted_at IS NULL`, newHash, tenantID, memberID, oldHash) if err != nil { return err } affected, err := result.RowsAffected() if err != nil { return err } if affected != 1 { return ErrConcurrentUpdate } return nil } func (s *Store) UpdateTradePassword(ctx context.Context, tenantID, memberID int64, oldHash, newHash string) error { result, err := s.db.ExecContext(ctx, `UPDATE hg_app_member SET trade_password_hash=?,updated_at=CURRENT_TIMESTAMP(3) WHERE tenant_id=? AND id=? AND trade_password_hash=? AND member_status=1 AND deleted_at IS NULL`, newHash, tenantID, memberID, oldHash) if err != nil { return err } affected, err := result.RowsAffected() if err != nil { return err } if affected != 1 { return ErrConcurrentUpdate } return nil }