276 lines
11 KiB
Go
276 lines
11 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
)
|
|
|
|
const publishStatusPublished = 3
|
|
|
|
type Banner struct {
|
|
ID int64 `db:"id"`
|
|
ImageURL string `db:"image_url"`
|
|
AltText string `db:"alt_text"`
|
|
TargetType int `db:"target_type"`
|
|
TargetValue string `db:"target_value"`
|
|
Sort int `db:"sort"`
|
|
}
|
|
|
|
type Content struct {
|
|
ContentKey string `db:"content_key"`
|
|
ContentType string `db:"content_type"`
|
|
Title string `db:"title"`
|
|
Summary string `db:"summary"`
|
|
BodyHTML string `db:"body_html"`
|
|
BodyText string `db:"body_text"`
|
|
CoverURL string `db:"cover_url"`
|
|
Version int `db:"version"`
|
|
PublishedAt string `db:"published_at"`
|
|
AssetIDs sql.NullString `db:"asset_ids"`
|
|
}
|
|
|
|
type MemberLevel struct {
|
|
ID int64 `db:"id"`
|
|
Code string `db:"level_code"`
|
|
Name string `db:"name"`
|
|
IconURL string `db:"icon_url"`
|
|
UpgradeAmount int64 `db:"upgrade_amount"`
|
|
GiftAmount int64 `db:"gift_amount"`
|
|
DailyOrderLimit int `db:"daily_order_limit"`
|
|
CommissionRate int `db:"commission_rate"`
|
|
DailyWithdrawLimit int `db:"daily_withdraw_limit"`
|
|
RuleJSON json.RawMessage `db:"rule_json"`
|
|
Version int `db:"version"`
|
|
Sort int `db:"sort"`
|
|
}
|
|
|
|
type MemberNotice struct {
|
|
ID int64 `db:"id"`
|
|
Title string `db:"title"`
|
|
Content string `db:"content"`
|
|
SourceType int `db:"source_type"`
|
|
StartAt string `db:"start_at"`
|
|
EndAt string `db:"end_at"`
|
|
CreatedAt string `db:"created_at"`
|
|
}
|
|
|
|
type MemberNoticePage struct {
|
|
Items []MemberNotice
|
|
Total int64
|
|
}
|
|
|
|
type TaskSummary struct {
|
|
TodayCount int `db:"today_count" json:"today_count"`
|
|
TodayCommissionAmount int64 `db:"today_commission_amount" json:"today_commission_amount"`
|
|
YesterdayCommissionAmount int64 `db:"yesterday_commission_amount" json:"yesterday_commission_amount"`
|
|
}
|
|
|
|
type LeaderboardMember struct {
|
|
MemberID int64 `db:"member_id"`
|
|
Username string `db:"username"`
|
|
AvatarURL string `db:"avatar_url"`
|
|
ClaimedPoints int64 `db:"claimed_points"`
|
|
}
|
|
|
|
type CustomerService struct {
|
|
ID int64 `db:"id"`
|
|
ServiceCode string `db:"service_code"`
|
|
Name string `db:"name"`
|
|
AvatarURL string `db:"avatar_url"`
|
|
WorkTime string `db:"work_time"`
|
|
OnlineStatus int `db:"online_status"`
|
|
ServiceURL string `db:"service_url"`
|
|
BackupContact string `db:"backup_contact"`
|
|
IMTenantCode string `db:"im_tenant_code"`
|
|
IMUserID string `db:"im_user_id"`
|
|
IMUsername string `db:"im_username"`
|
|
}
|
|
|
|
func (s *Store) CurrentSiteConfig(ctx context.Context, tenantID int64) (json.RawMessage, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
var raw []byte
|
|
err := s.db.GetContext(ctx, &raw, `SELECT config_json FROM hg_site_config_version
|
|
WHERE tenant_id=? AND publish_status=? AND (effective_at IS NULL OR effective_at<=UTC_TIMESTAMP(3))
|
|
ORDER BY COALESCE(effective_at,published_at,created_at) DESC,id DESC LIMIT 1`, tenantID, publishStatusPublished)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return json.RawMessage(`{}`), nil
|
|
}
|
|
return json.RawMessage(raw), err
|
|
}
|
|
|
|
func (s *Store) ActiveBanners(ctx context.Context, tenantID int64, position string) ([]Banner, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
rows := make([]Banner, 0)
|
|
err := s.db.SelectContext(ctx, &rows, `SELECT id,image_url,alt_text,target_type,COALESCE(target_value,'') AS target_value,sort
|
|
FROM hg_banner WHERE tenant_id=? AND position=? AND publish_status=?
|
|
AND (start_at IS NULL OR start_at<=UTC_TIMESTAMP(3)) AND (end_at IS NULL OR end_at>UTC_TIMESTAMP(3))
|
|
AND deleted_at IS NULL ORDER BY sort ASC,id ASC`, tenantID, position, publishStatusPublished)
|
|
return rows, err
|
|
}
|
|
|
|
func (s *Store) PublishedContent(ctx context.Context, tenantID int64, key string) (*Content, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
var row Content
|
|
err := s.db.GetContext(ctx, &row, `SELECT c.content_key,c.content_type,v.title,COALESCE(v.summary,'') AS summary,
|
|
v.body_html,COALESCE(v.body_text,'') AS body_text,COALESCE(v.cover_url,'') AS cover_url,
|
|
v.version_no AS version,COALESCE(DATE_FORMAT(c.published_at,'%Y-%m-%dT%H:%i:%sZ'),'') AS published_at,v.asset_ids
|
|
FROM hg_content c JOIN hg_content_version v
|
|
ON v.tenant_id=c.tenant_id AND v.id=c.published_version_id
|
|
WHERE c.tenant_id=? AND c.content_key=? AND c.publish_status=? AND v.publish_status=?
|
|
AND (v.effective_at IS NULL OR v.effective_at<=UTC_TIMESTAMP(3))
|
|
AND c.deleted_at IS NULL LIMIT 1`, tenantID, key, publishStatusPublished, publishStatusPublished)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrResourceNotFound
|
|
}
|
|
return &row, err
|
|
}
|
|
|
|
func (s *Store) ActiveMemberLevels(ctx context.Context, tenantID int64) ([]MemberLevel, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
rows := make([]MemberLevel, 0)
|
|
err := s.db.SelectContext(ctx, &rows, `SELECT l.id,l.level_code,l.name,l.icon_url,l.sort,
|
|
v.upgrade_amount,v.gift_amount,v.daily_order_limit,v.commission_rate,v.daily_withdraw_limit,
|
|
COALESCE(v.rule_json,JSON_OBJECT()) AS rule_json,v.version_no AS version
|
|
FROM hg_app_member_level l JOIN hg_app_member_level_version v
|
|
ON v.tenant_id=l.tenant_id AND v.id=l.current_version_id
|
|
WHERE l.tenant_id=? AND l.status=1 AND v.publish_status=?
|
|
AND (v.effective_at IS NULL OR v.effective_at<=UTC_TIMESTAMP(3))
|
|
AND l.deleted_at IS NULL ORDER BY l.sort ASC,l.id ASC`, tenantID, publishStatusPublished)
|
|
return rows, err
|
|
}
|
|
|
|
func (s *Store) ActiveMemberNotices(ctx context.Context, tenantID, memberID int64, limit int) ([]MemberNotice, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
rows := make([]MemberNotice, 0)
|
|
err := s.db.SelectContext(ctx, &rows, `SELECT id,title,content,source_type,
|
|
DATE_FORMAT(start_at,'%Y-%m-%dT%H:%i:%sZ') AS start_at,
|
|
DATE_FORMAT(end_at,'%Y-%m-%dT%H:%i:%sZ') AS end_at,
|
|
DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at
|
|
FROM hg_app_member_notice WHERE tenant_id=? AND member_id IN (0,?) AND notice_status=1
|
|
AND start_at<=UTC_TIMESTAMP(3) AND end_at>UTC_TIMESTAMP(3)
|
|
ORDER BY start_at DESC,id DESC LIMIT ?`, tenantID, memberID, limit)
|
|
return rows, err
|
|
}
|
|
|
|
func (s *Store) LatestPopupNotice(ctx context.Context, tenantID, memberID int64) (*MemberNotice, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
var row MemberNotice
|
|
err := s.db.GetContext(ctx, &row, `SELECT id,title,content,source_type,
|
|
DATE_FORMAT(start_at,'%Y-%m-%dT%H:%i:%sZ') AS start_at,
|
|
DATE_FORMAT(end_at,'%Y-%m-%dT%H:%i:%sZ') AS end_at,
|
|
DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at
|
|
FROM hg_app_member_notice
|
|
WHERE tenant_id=? AND member_id IN (0,?) AND notice_status=1
|
|
AND start_at<=UTC_TIMESTAMP(3) AND end_at>UTC_TIMESTAMP(3)
|
|
ORDER BY start_at DESC,id DESC LIMIT 1`, tenantID, memberID)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return &row, err
|
|
}
|
|
|
|
func (s *Store) MarkMemberNoticeRead(ctx context.Context, tenantID, memberID, noticeID int64) error {
|
|
if s == nil || s.db == nil {
|
|
return errors.New("store is unavailable")
|
|
}
|
|
result, err := s.db.ExecContext(ctx, `INSERT INTO hg_app_member_notice_read
|
|
(tenant_id,notice_id,member_id,read_at,created_at)
|
|
SELECT n.tenant_id,n.id,?,UTC_TIMESTAMP(3),UTC_TIMESTAMP(3)
|
|
FROM hg_app_member_notice n
|
|
WHERE n.tenant_id=? AND n.id=? AND n.member_id IN (0,?) AND n.notice_status=1
|
|
AND n.start_at<=UTC_TIMESTAMP(3) AND n.end_at>UTC_TIMESTAMP(3)
|
|
ON DUPLICATE KEY UPDATE read_at=VALUES(read_at)`, memberID, tenantID, noticeID, memberID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if affected == 0 {
|
|
return ErrResourceNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) MemberNotices(ctx context.Context, tenantID, memberID int64, offset, limit int) (*MemberNoticePage, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
const where = ` WHERE tenant_id=? AND member_id IN (0,?) AND notice_status=1
|
|
AND start_at<=UTC_TIMESTAMP(3) AND end_at>UTC_TIMESTAMP(3)`
|
|
args := []any{tenantID, memberID}
|
|
var total int64
|
|
if err := s.db.GetContext(ctx, &total, `SELECT COUNT(*) FROM hg_app_member_notice`+where, args...); err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]MemberNotice, 0)
|
|
args = append(args, limit, offset)
|
|
err := s.db.SelectContext(ctx, &items, `SELECT id,title,content,source_type,
|
|
DATE_FORMAT(start_at,'%Y-%m-%dT%H:%i:%sZ') AS start_at,
|
|
DATE_FORMAT(end_at,'%Y-%m-%dT%H:%i:%sZ') AS end_at,
|
|
DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%sZ') AS created_at
|
|
FROM hg_app_member_notice`+where+` ORDER BY created_at DESC,id DESC LIMIT ? OFFSET ?`, args...)
|
|
return &MemberNoticePage{Items: items, Total: total}, err
|
|
}
|
|
|
|
func (s *Store) MemberTaskSummary(ctx context.Context, tenantID, memberID int64) (*TaskSummary, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
var row TaskSummary
|
|
err := s.db.GetContext(ctx, &row, `SELECT
|
|
(SELECT COUNT(*) FROM hg_trade_order o WHERE o.tenant_id=? AND o.member_id=?
|
|
AND o.created_at>=UTC_DATE() AND o.created_at<UTC_DATE()+INTERVAL 1 DAY) AS today_count,
|
|
(SELECT COALESCE(SUM(t.commission_amount),0) FROM hg_task_submission t WHERE t.tenant_id=? AND t.member_id=?
|
|
AND t.submission_status=4 AND t.settled_at>=UTC_DATE() AND t.settled_at<UTC_DATE()+INTERVAL 1 DAY) AS today_commission_amount,
|
|
(SELECT COALESCE(SUM(y.commission_amount),0) FROM hg_task_submission y WHERE y.tenant_id=? AND y.member_id=?
|
|
AND y.submission_status=4 AND y.settled_at>=UTC_DATE()-INTERVAL 1 DAY AND y.settled_at<UTC_DATE()) AS yesterday_commission_amount`,
|
|
tenantID, memberID, tenantID, memberID, tenantID, memberID)
|
|
return &row, err
|
|
}
|
|
|
|
func (s *Store) TaskLeaderboard(ctx context.Context, tenantID int64, limit int) ([]LeaderboardMember, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
rows := make([]LeaderboardMember, 0)
|
|
err := s.db.SelectContext(ctx, &rows, `SELECT m.id AS member_id,m.username,
|
|
COALESCE(a.file_url,'') AS avatar_url,COALESCE(SUM(o.total_amount),0) AS claimed_points
|
|
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
|
|
JOIN hg_trade_order o ON o.tenant_id=m.tenant_id AND o.member_id=m.id
|
|
AND o.order_type=2 AND o.order_status=4
|
|
WHERE m.tenant_id=? AND m.member_status=1 AND m.deleted_at IS NULL
|
|
GROUP BY m.id,m.username,a.file_url
|
|
ORDER BY claimed_points DESC,m.id ASC LIMIT ?`, tenantID, limit)
|
|
return rows, err
|
|
}
|
|
|
|
func (s *Store) ActiveCustomerServices(ctx context.Context, tenantID int64) ([]CustomerService, error) {
|
|
if s == nil || s.db == nil {
|
|
return nil, errors.New("store is unavailable")
|
|
}
|
|
rows := make([]CustomerService, 0)
|
|
err := s.db.SelectContext(ctx, &rows, `SELECT id,service_code,name,avatar_url,work_time,
|
|
online_status,service_url,backup_contact,im_tenant_code,COALESCE(im_user_id,'') AS im_user_id,
|
|
COALESCE(im_username,'') AS im_username FROM hg_customer_service
|
|
WHERE tenant_id=? AND status=1 AND deleted_at IS NULL ORDER BY sort ASC,id ASC`, tenantID)
|
|
return rows, err
|
|
}
|