@@ -0,0 +1,45 @@
|
||||
package achievement
|
||||
|
||||
import (
|
||||
apiachievement "honoka-chan/internal/handler/api/achievement"
|
||||
"honoka-chan/internal/middleware"
|
||||
"honoka-chan/internal/router"
|
||||
achievementschema "honoka-chan/internal/schema/achievement"
|
||||
"honoka-chan/internal/session"
|
||||
honokautils "honoka-chan/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const pagingAccomplishedPageSize = 10
|
||||
|
||||
func pagingAccomplishedList(ctx *gin.Context) {
|
||||
ss := session.Get(ctx)
|
||||
defer ss.Finalize()
|
||||
|
||||
req := achievementschema.PagingAccomplishedListReq{}
|
||||
err := honokautils.ParseRequestData(ctx, &req)
|
||||
if ss.CheckErr(err) {
|
||||
return
|
||||
}
|
||||
|
||||
grouped, err := apiachievement.ListVisibleAccomplishedAchievements(ss)
|
||||
if ss.CheckErr(err) {
|
||||
return
|
||||
}
|
||||
|
||||
items := grouped[req.FilterCategoryID]
|
||||
start := min(max(req.FromCount, 0), len(items))
|
||||
|
||||
end := min(start+pagingAccomplishedPageSize, len(items))
|
||||
|
||||
ss.Respond(achievementschema.PagingAccomplishedListResp{
|
||||
ResponseData: items[start:end],
|
||||
ReleaseInfo: []any{},
|
||||
StatusCode: 200,
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
router.AddHandler("main.php", "POST", "/achievement/pagingAccomplishedList", middleware.Common, pagingAccomplishedList)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package achievement
|
||||
|
||||
import (
|
||||
honokautils "honoka-chan/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var achievementFilterCategoryList = []int{
|
||||
1, // 重要
|
||||
3, // 挑战
|
||||
4, // 每日
|
||||
5, // 期间限定
|
||||
6, // 8周年限定/全世界5000万人突破纪念
|
||||
7, // 回忆画廊
|
||||
8, // 9周年限定
|
||||
9, // 连携课题
|
||||
}
|
||||
|
||||
func AchievementApi(ctx *gin.Context, action string) (res any, err error) {
|
||||
switch action {
|
||||
case "unaccomplishList":
|
||||
res, err = unaccomplishList()
|
||||
case "initialAccomplishedList":
|
||||
res, err = initialAccomplishedList(ctx)
|
||||
default:
|
||||
err = honokautils.NewUnimplementedActionError("achievement", action)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package achievement
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
achievementapischema "honoka-chan/internal/schema/api/achievement"
|
||||
"honoka-chan/internal/session"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type achievementRow struct {
|
||||
AchievementID int `xorm:"achievement_id"`
|
||||
AchievementFilterCategoryID int `xorm:"achievement_filter_category_id"`
|
||||
DisplayFlag int `xorm:"display_flag"`
|
||||
StartDate string
|
||||
EndDate *string
|
||||
}
|
||||
|
||||
func ListVisibleAccomplishedAchievements(ss *session.Session) (map[int][]achievementapischema.AchievementListItem, error) {
|
||||
var achievementList []achievementRow
|
||||
err := ss.MainEng.Table("achievement_m").
|
||||
Cols("achievement_id,achievement_filter_category_id,display_flag,start_date,end_date").
|
||||
Where("display_flag = ?", 1).
|
||||
Find(&achievementList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
grouped := make(map[int][]achievementapischema.AchievementListItem, len(achievementFilterCategoryList))
|
||||
for _, achievement := range achievementList {
|
||||
if !isAchievementVisible(now, achievement.StartDate, achievement.EndDate) {
|
||||
continue
|
||||
}
|
||||
|
||||
grouped[achievement.AchievementFilterCategoryID] = append(
|
||||
grouped[achievement.AchievementFilterCategoryID],
|
||||
achievementapischema.AchievementListItem{
|
||||
AchievementID: achievement.AchievementID,
|
||||
Count: 1,
|
||||
IsAccomplished: true,
|
||||
InsertDate: normalizeAchievementDate(achievement.StartDate),
|
||||
EndDate: normalizeAchievementDatePtr(achievement.EndDate),
|
||||
RemainingTime: "",
|
||||
IsNew: false,
|
||||
ForDisplay: achievement.DisplayFlag != 0,
|
||||
RewardList: []achievementapischema.AchievementRewardItem{
|
||||
{
|
||||
AddType: 3001,
|
||||
ItemID: 4,
|
||||
Amount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for filterCategoryID, items := range grouped {
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
return items[i].InsertDate > items[j].InsertDate
|
||||
})
|
||||
grouped[filterCategoryID] = items
|
||||
}
|
||||
|
||||
return grouped, nil
|
||||
}
|
||||
|
||||
func normalizeAchievementDate(value string) string {
|
||||
if value == "" {
|
||||
return "1970-01-01 00:00:00"
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(value, "/", "-")
|
||||
}
|
||||
|
||||
func normalizeAchievementDatePtr(value *string) *string {
|
||||
if value == nil || *value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
normalized := normalizeAchievementDate(*value)
|
||||
return &normalized
|
||||
}
|
||||
|
||||
func isAchievementVisible(now time.Time, startDate string, endDate *string) bool {
|
||||
start, err := parseAchievementTime(startDate)
|
||||
if err == nil && now.Before(start) {
|
||||
return false
|
||||
}
|
||||
|
||||
if endDate == nil || *endDate == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
end, err := parseAchievementTime(*endDate)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return !now.After(end)
|
||||
}
|
||||
|
||||
func parseAchievementTime(value string) (time.Time, error) {
|
||||
layouts := []string{
|
||||
"2006/01/02 15:04:05",
|
||||
"2006/1/2 15:04:05",
|
||||
}
|
||||
|
||||
for _, layout := range layouts {
|
||||
parsed, err := time.ParseInLocation(layout, value, time.Local)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
}
|
||||
|
||||
return time.Time{}, fmt.Errorf("invalid achievement time: %s", value)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package achievement
|
||||
|
||||
import (
|
||||
achievementapischema "honoka-chan/internal/schema/api/achievement"
|
||||
"honoka-chan/internal/session"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initialAccomplishedList(ctx *gin.Context) (res any, err error) {
|
||||
ss := session.Get(ctx)
|
||||
|
||||
grouped, err := ListVisibleAccomplishedAchievements(ss)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listData := make([]achievementapischema.UnaccomplishListData, 0, len(achievementFilterCategoryList))
|
||||
for _, filterCategoryID := range achievementFilterCategoryList {
|
||||
achievementItems := grouped[filterCategoryID]
|
||||
if achievementItems == nil {
|
||||
achievementItems = []achievementapischema.AchievementListItem{}
|
||||
} else {
|
||||
if len(achievementItems) > 5 {
|
||||
achievementItems = achievementItems[:5]
|
||||
}
|
||||
}
|
||||
|
||||
listData = append(listData, achievementapischema.UnaccomplishListData{
|
||||
FilterCategoryID: filterCategoryID,
|
||||
AchievementList: achievementItems,
|
||||
Count: len(grouped[filterCategoryID]),
|
||||
IsLast: true,
|
||||
})
|
||||
}
|
||||
|
||||
return achievementapischema.UnaccomplishListResp{
|
||||
Result: listData,
|
||||
Status: 200,
|
||||
CommandNum: false,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package achievement
|
||||
|
||||
import (
|
||||
achievementapischema "honoka-chan/internal/schema/api/achievement"
|
||||
"time"
|
||||
)
|
||||
|
||||
func unaccomplishList() (res any, err error) {
|
||||
listData := make([]achievementapischema.UnaccomplishListData, 0, len(achievementFilterCategoryList))
|
||||
for i := range achievementFilterCategoryList {
|
||||
listData = append(listData, achievementapischema.UnaccomplishListData{
|
||||
FilterCategoryID: achievementFilterCategoryList[i],
|
||||
AchievementList: []achievementapischema.AchievementListItem{},
|
||||
Count: 0,
|
||||
IsLast: true,
|
||||
})
|
||||
}
|
||||
|
||||
res = achievementapischema.UnaccomplishListResp{
|
||||
Result: listData,
|
||||
Status: 200,
|
||||
CommandNum: false,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"honoka-chan/internal/constant"
|
||||
"honoka-chan/internal/handler/api/achievement"
|
||||
"honoka-chan/internal/handler/api/album"
|
||||
"honoka-chan/internal/handler/api/award"
|
||||
"honoka-chan/internal/handler/api/background"
|
||||
@@ -57,6 +58,8 @@ func api(ctx *gin.Context) {
|
||||
switch v.Module {
|
||||
case "album":
|
||||
result, err = album.AlbumApi(ctx, v.Action)
|
||||
case "achievement":
|
||||
result, err = achievement.AchievementApi(ctx, v.Action)
|
||||
case "award":
|
||||
result, err = award.AwardApi(ctx, v.Action)
|
||||
case "background":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
_ "honoka-chan/internal/handler/achievement"
|
||||
_ "honoka-chan/internal/handler/album"
|
||||
_ "honoka-chan/internal/handler/announce"
|
||||
_ "honoka-chan/internal/handler/api"
|
||||
|
||||
Reference in New Issue
Block a user