diff --git a/ReadMe.md b/ReadMe.md
index 0e1c351..c2b12d3 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -127,7 +127,7 @@ git push origin master --tags
 [7]: https://typeorm.io/
 [8]: https://swagger.io/
 [9]: https://github.com/anttiviljami/openapi-backend
-[10]: https://github.com/settings/tokens
+[10]: https://github.com/settings/tokens/new?description=KYS-service&scopes=read:packages
 [11]: https://www.postgresql.org/
 [12]: https://azure.microsoft.com/en-us/products/storage/blobs
 [13]: https://www.leancloud.cn/
diff --git a/package.json b/package.json
index 70ec4c9..a60f4fb 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
         "web-utility": "^4.4.2"
     },
     "devDependencies": {
+        "@kaiyuanshe/kys-service": "1.0.0-rc.0",
         "@types/jsonwebtoken": "^9.0.7",
         "@types/koa": "^2.15.0",
         "@types/koa-bodyparser": "^4.3.12",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b08f70f..ecd8879 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -108,6 +108,9 @@ importers:
         specifier: ^4.4.2
         version: 4.4.2(typescript@5.7.2)
     devDependencies:
+      '@kaiyuanshe/kys-service':
+        specifier: 1.0.0-rc.0
+        version: 1.0.0-rc.0(jsdom@25.0.1)(mobx@6.13.5)(typescript@5.7.2)(web-fetch@1.4.2(@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0))(@types/node@20.17.7))
       '@types/jsonwebtoken':
         specifier: ^9.0.7
         version: 9.0.7
@@ -330,6 +333,11 @@ packages:
   '@jsdevtools/ono@7.1.3':
     resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
 
+  '@kaiyuanshe/kys-service@1.0.0-rc.0':
+    resolution: {integrity: sha512-LW1OeD5RuNj3mI61yoGzdgQEXf7KbWuHEBx0ntfmpDp/oiHxr8xG00oS5+PQ2sFMZgCSzeqOU+315dnJFsDGXg==, tarball: https://npm.pkg.github.com/download/@kaiyuanshe/kys-service/1.0.0-rc.0/e6e370c453f3a99aa961bccd5f56e84e7e0d8e2c}
+    peerDependencies:
+      web-fetch: ^1.4.0
+
   '@koa/cors@5.0.0':
     resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==}
     engines: {node: '>= 14.0.0'}
@@ -3251,6 +3259,17 @@ snapshots:
 
   '@jsdevtools/ono@7.1.3': {}
 
+  '@kaiyuanshe/kys-service@1.0.0-rc.0(jsdom@25.0.1)(mobx@6.13.5)(typescript@5.7.2)(web-fetch@1.4.2(@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0))(@types/node@20.17.7))':
+    dependencies:
+      '@types/jsonwebtoken': 9.0.7
+      '@types/koa': 2.15.0
+      mobx-restful: 2.0.0(jsdom@25.0.1)(mobx@6.13.5)(typescript@5.7.2)
+      web-fetch: 1.4.2(@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0))(@types/node@20.17.7)
+    transitivePeerDependencies:
+      - jsdom
+      - mobx
+      - typescript
+
   '@koa/cors@5.0.0':
     dependencies:
       vary: 1.1.2
diff --git a/src/controller/CheckEvent.ts b/src/controller/CheckEvent.ts
index 5e55bd4..918d727 100644
--- a/src/controller/CheckEvent.ts
+++ b/src/controller/CheckEvent.ts
@@ -6,22 +6,27 @@ import {
     ForbiddenError,
     Get,
     JsonController,
+    Param,
     Post,
-    QueryParams
-} from 'routing-controllers';
-import { ResponseSchema } from 'routing-controllers-openapi';
+    QueryParams,
+} from "routing-controllers";
+import { ResponseSchema } from "routing-controllers-openapi";
 
 import {
+    ActivityCheckInListChunk,
+    BaseFilter,
     CheckEvent,
     CheckEventChunk,
     CheckEventFilter,
     CheckEventInput,
+    dataSource,
     User,
-    dataSource
-} from '../model';
-import { ActivityLogController } from './ActivityLog';
+    UserActivityCheckInListChunk,
+} from "../model";
+import { ActivityLogController } from "./ActivityLog";
+import { FindOptionsWhere } from "typeorm";
 
-@JsonController('/event/check')
+@JsonController("/event/check")
 export class CheckEventController {
     store = dataSource.getRepository(CheckEvent);
     userStore = dataSource.getRepository(User);
@@ -31,26 +36,26 @@ export class CheckEventController {
     @ResponseSchema(CheckEvent)
     async createOne(
         @CurrentUser() createdBy: User,
-        @Body() { user: id, ...data }: CheckEventInput
+        @Body() { user: id, ...data }: CheckEventInput,
     ) {
-        if (createdBy.id === id) throw new ForbiddenError('No self-checking');
+        if (createdBy.id === id) throw new ForbiddenError("No self-checking");
 
         const user = await this.userStore.findOne({ where: { id } });
 
-        if (!user) throw new BadRequestError('Invalid user: ' + id);
+        if (!user) throw new BadRequestError("Invalid user: " + id);
 
         const checked = await this.store.findOne({
-            where: { ...data, user: { id } }
+            where: { ...data, user: { id } },
         });
 
-        if (checked) throw new ForbiddenError('No duplicated check');
+        if (checked) throw new ForbiddenError("No duplicated check");
 
         const saved = await this.store.save({ ...data, createdBy, user });
 
         await ActivityLogController.logCreate(
             createdBy,
-            'CheckEvent',
-            saved.id
+            "CheckEvent",
+            saved.id,
         );
         return saved;
     }
@@ -58,24 +63,70 @@ export class CheckEventController {
     @Get()
     @ResponseSchema(CheckEventChunk)
     async getSessionList(
-        @QueryParams()
-        {
+        @QueryParams() {
             user: id,
             activityId,
             agendaId,
             pageSize = 10,
-            pageIndex = 1
-        }: CheckEventFilter
+            pageIndex = 1,
+        }: CheckEventFilter,
     ) {
         const [list, count] = await this.store.findAndCount({
             where: {
                 ...(id ? { user: { id } } : {}),
                 activityId,
-                agendaId
+                agendaId,
             },
-            relations: ['user'],
+            relations: ["user"],
             skip: pageSize * (pageIndex - 1),
-            take: pageSize
+            take: pageSize,
+        });
+        return { list, count };
+    }
+
+    @Get("/user/:id")
+    @ResponseSchema(UserActivityCheckInListChunk)
+    getCheckEventList(
+        @Param("id") id: number,
+        @QueryParams() filter: BaseFilter,
+    ) {
+        return this.queryList(
+            { user: { id } },
+            filter,
+            ["user"],
+        );
+    }
+
+    @Get("/activity/:id")
+    @ResponseSchema(ActivityCheckInListChunk)
+    getActivityCheckEventList(
+        @Param("id") id: string,
+        @QueryParams() filter: BaseFilter,
+    ) {
+        return this.queryList({ activityId: id }, filter);
+    }
+
+    @Get("/activity")
+    @ResponseSchema(ActivityCheckInListChunk)
+    getAgendaCheckEventList(
+        @Param("id") id: string,
+        @QueryParams() filter: BaseFilter,
+    ) {
+        return this.queryList({ agendaId: id }, filter);
+    }
+
+    async queryList(
+        where: FindOptionsWhere<CheckEvent>,
+        { pageSize, pageIndex, sort }: BaseFilter,
+        relations?: string[],
+    ) {
+        const skip = pageSize * (pageIndex - 1);
+
+        const [list, count] = await this.store.findAndCount({
+            where,
+            relations,
+            skip,
+            take: pageSize,
         });
         return { list, count };
     }
diff --git a/src/model/ActivityLog.ts b/src/model/ActivityLog.ts
index d568e3e..4910a5d 100644
--- a/src/model/ActivityLog.ts
+++ b/src/model/ActivityLog.ts
@@ -1,38 +1,38 @@
-import { Type } from 'class-transformer';
+import { Type } from "class-transformer";
 import {
     IsEnum,
     IsInt,
     IsObject,
     IsOptional,
     Min,
-    ValidateNested
-} from 'class-validator';
-import { Column, Entity, ViewColumn, ViewEntity } from 'typeorm';
+    ValidateNested,
+} from "class-validator";
+import { Column, Entity, ViewColumn, ViewEntity } from "typeorm";
 
-import { Base, BaseFilter, InputData, ListChunk } from './Base';
-import { CheckEvent } from './CheckEvent';
-import { User, UserBase } from './User';
+import { Base, BaseFilter, InputData, ListChunk } from "./Base";
+import { CheckEvent } from "./CheckEvent";
+import { User, UserBase } from "./User";
 
 export enum Operation {
-    Create = 'create',
-    Update = 'update',
-    Delete = 'delete'
+    Create = "create",
+    Update = "update",
+    Delete = "delete",
 }
 
 export const LogableTable = { User, CheckEvent };
 
 const LogableTableEnum = Object.fromEntries(
-    Object.entries(LogableTable).map(([key]) => [key, key])
+    Object.entries(LogableTable).map(([key]) => [key, key]),
 );
 
 @Entity()
 export class ActivityLog extends UserBase {
     @IsEnum(Operation)
-    @Column({ type: 'simple-enum', enum: Operation })
+    @Column({ type: "simple-enum", enum: Operation })
     operation: Operation;
 
     @IsEnum(LogableTableEnum)
-    @Column({ type: 'simple-enum', enum: LogableTableEnum })
+    @Column({ type: "simple-enum", enum: LogableTableEnum })
     tableName: keyof typeof LogableTable;
 
     @IsInt()
@@ -45,10 +45,8 @@ export class ActivityLog extends UserBase {
     record?: Base;
 }
 
-export class ActivityLogFilter
-    extends BaseFilter
-    implements Partial<InputData<ActivityLog>>
-{
+export class ActivityLogFilter extends BaseFilter
+    implements Partial<InputData<ActivityLog>> {
     @IsEnum(Operation)
     @IsOptional()
     operation?: Operation;
@@ -65,13 +63,13 @@ export class ActivityLogListChunk implements ListChunk<ActivityLog> {
 }
 
 @ViewEntity({
-    expression: connection =>
+    expression: (connection) =>
         connection
             .createQueryBuilder()
-            .from(ActivityLog, 'al')
-            .groupBy('al.createdBy')
-            .select('al.createdBy.id', 'userId')
-            .addSelect('COUNT(al.id)', 'score')
+            .from(ActivityLog, "al")
+            .groupBy("al.createdBy")
+            .select("al.createdBy.id", "userId")
+            .addSelect("COUNT(al.id)", "score"),
 })
 export class UserRank {
     @IsInt()
diff --git a/src/model/Base.ts b/src/model/Base.ts
index e77f92b..316be71 100644
--- a/src/model/Base.ts
+++ b/src/model/Base.ts
@@ -1,17 +1,23 @@
-import { Type } from 'class-transformer';
+import { Type } from "class-transformer";
 import {
     IsDateString,
+    IsEnum,
     IsInt,
     IsOptional,
     IsString,
-    Min
-} from 'class-validator';
-import { NewData } from 'mobx-restful';
+    Min,
+} from "class-validator";
+import { NewData } from "mobx-restful";
 import {
     CreateDateColumn,
     PrimaryGeneratedColumn,
-    UpdateDateColumn
-} from 'typeorm';
+    UpdateDateColumn,
+} from "typeorm";
+
+export enum Sort {
+    DESC = "DESC",
+    ASC = "ASC",
+}
 
 export abstract class Base {
     constructor(id?: number) {
@@ -52,6 +58,10 @@ export class BaseFilter {
     @IsString()
     @IsOptional()
     keywords?: string;
+
+    @IsEnum(Sort)
+    @IsOptional()
+    sort?: Sort;
 }
 
 export interface ListChunk<T> {
diff --git a/src/model/CheckEvent.ts b/src/model/CheckEvent.ts
index 9782efa..220020e 100644
--- a/src/model/CheckEvent.ts
+++ b/src/model/CheckEvent.ts
@@ -1,16 +1,17 @@
-import { Type } from 'class-transformer';
+import { Type } from "class-transformer";
 import {
+    IsEnum,
     IsInt,
     IsLatLong,
     IsOptional,
     IsString,
     Min,
-    ValidateNested
-} from 'class-validator';
-import { Column, Entity, ManyToOne } from 'typeorm';
+    ValidateNested,
+} from "class-validator";
+import { Column, Entity, ManyToOne, ViewColumn, ViewEntity } from "typeorm";
 
-import { BaseFilter, ListChunk } from './Base';
-import { User, UserBase, UserInputData } from './User';
+import { BaseFilter, ListChunk } from "./Base";
+import { User, UserBase, UserInputData } from "./User";
 
 @Entity()
 export class CheckEvent extends UserBase {
@@ -63,10 +64,8 @@ export class CheckEventInput implements UserInputData<CheckEvent> {
     agendaTitle: string;
 }
 
-export class CheckEventFilter
-    extends BaseFilter
-    implements Partial<BaseFilter & CheckEventInput>
-{
+export class CheckEventFilter extends BaseFilter
+    implements Partial<BaseFilter & CheckEventInput> {
     @IsInt()
     @Min(1)
     @IsOptional()
@@ -90,3 +89,115 @@ export class CheckEventChunk implements ListChunk<CheckEvent> {
     @ValidateNested({ each: true })
     list: CheckEvent[];
 }
+
+@ViewEntity({
+    expression: (connection) =>
+        connection
+            .createQueryBuilder()
+            .from(CheckEvent, "ce")
+            .groupBy("ce.user, ce.activityId")
+            .select("ce.user", "user")
+            .addSelect("ce.activityId", "activityId")
+            .addSelect("ce.activityName", "activityName")
+            .addSelect("COUNT(ce.id)", "checkCount"),
+})
+export class UserActivityCheckInSummary {
+    @Type(() => User)
+    @ValidateNested()
+    @ViewColumn()
+    user: User;
+
+    @ViewColumn()
+    activityId: string;
+
+    @ViewColumn()
+    activityName: string;
+
+    @ViewColumn()
+    checkCount: number;
+}
+
+@ViewEntity({
+    expression: (connection) =>
+        connection
+            .createQueryBuilder()
+            .from(CheckEvent, "ce")
+            .groupBy("ce.activityId, ce.agendaId")
+            .select("ce.activityId", "activityId")
+            .addSelect("ce.activityName", "activityName")
+            .addSelect("ce.agendaId", "agendaId")
+            .addSelect("ce.agendaTitle", "agendaTitle")
+            .addSelect("COUNT(ce.id)", "checkCount"),
+})
+export class ActivityAgendaCheckInSummary {
+    @ViewColumn()
+    activityId: string;
+
+    @ViewColumn()
+    activityName: string;
+
+    @ViewColumn()
+    agendaId: string;
+
+    @ViewColumn()
+    agendaTitle: string;
+
+    @ViewColumn()
+    checkCount: number;
+}
+
+@ViewEntity({
+    expression: (connection) =>
+        connection
+            .createQueryBuilder()
+            .from(CheckEvent, "ce")
+            .groupBy("ce.activityId")
+            .select("ce.activityId", "activityId")
+            .addSelect("ce.activityName", "activityName")
+            .addSelect("COUNT(ce.id)", "checkCount"),
+})
+export class ActivityCheckInSummary {
+    @IsInt()
+    @Min(1)
+    @ViewColumn()
+    activityId: number;
+
+    @ViewColumn()
+    activityName: string;
+
+    @ViewColumn()
+    checkCount: number;
+}
+
+export class UserActivityCheckInListChunk
+    implements ListChunk<UserActivityCheckInSummary> {
+    @IsInt()
+    @Min(0)
+    count: number;
+
+    @Type(() => UserActivityCheckInSummary)
+    @ValidateNested({ each: true })
+    list: UserActivityCheckInSummary[];
+}
+
+export class ActivityAgendaCheckInListChunk
+    implements ListChunk<ActivityAgendaCheckInSummary> {
+    @IsInt()
+    @Min(0)
+    count: number;
+
+    @Type(() => ActivityAgendaCheckInSummary)
+    @ValidateNested({ each: true })
+    list: ActivityAgendaCheckInSummary[];
+}
+
+export class ActivityCheckInListChunk
+    implements ListChunk<ActivityCheckInSummary> {
+    @IsInt()
+    @Min(0)
+    count: number;
+
+    @Type(() => ActivityCheckInSummary)
+    @ValidateNested({ each: true })
+    list: ActivityCheckInSummary[];
+}