feat(前端): 测试环境登录框支持自动填充测试账号

通过 VITE_AUTO_FILL_TEST 环境变量控制,在 .env.test 中启用,
使测试环境构建后登录框也能自动填充测试账号,方便测试人员使用。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
En 2026-04-11 17:03:22 +08:00
parent 9f3040ad3a
commit 98e9ad1d28
574 changed files with 2377 additions and 1322 deletions

View File

@ -10,25 +10,25 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
| 目录 | 说明 |
|------|------|
| `backend-java/` | Spring Boot 后端(实际开发目录) |
| `frontend/` | Vue 3 前端(实际开发目录) |
| `lesingle-creation-backend/` | Spring Boot 后端(实际开发目录) |
| `lesingle-creation-frontend/` | Vue 3 前端(实际开发目录) |
| `lesingle-aicreate-client/` | AI 绘本创作客户端(独立模块) |
## 常用命令
### 后端 (backend-java/)
### 后端 (lesingle-creation-backend/)
```bash
cd backend-java
cd lesingle-creation-backend
mvn spring-boot:run -Dspring.profiles.active=dev # 开发启动(端口 8580上下文 /api
mvn flyway:migrate # 执行数据库迁移
mvn clean package # 构建打包
```
### 前端 (frontend/)
### 前端 (lesingle-creation-frontend/)
```bash
cd frontend
cd lesingle-creation-frontend
npm run dev # 开发模式(端口 3000代理 /api → localhost:8580
npm run build # 生产构建base: /web/
npm run build:test # 测试环境构建base: /web-test/
@ -60,9 +60,9 @@ npm install && npm run dev # 独立启动
| 富文本 | WangEditor |
| 图表 | ECharts |
## 后端架构 (backend-java/)
## 后端架构 (lesingle-creation-backend/)
### 基础包: `com.competition`
### 基础包: `com.lesingle`
### 三层架构
@ -77,7 +77,7 @@ npm install && npm run dev # 独立启动
### 模块划分
```
com.competition.modules/
com.lesingle.modules/
├── biz/
│ ├── contest/ # 赛事管理(/contests
│ ├── homework/ # 作业管理(/homework
@ -122,7 +122,7 @@ PageResult<T> → {list, total, page, pageSize}
- 命名: `V{number}__description.sql`(注意双下划线)
- 不使用外键约束,关联关系通过代码控制
## 前端架构 (frontend/)
## 前端架构 (lesingle-creation-frontend/)
### 路由与多租户

View File

@ -101,7 +101,7 @@ pnpm install
3. 配置环境变量,创建 `.env` 文件:
```env
DATABASE_URL="mysql://user:password@localhost:3306/competition_management?schema=public"
DATABASE_URL="mysql://user:password@localhost:3306/lesingle-creation-test?schema=public"
JWT_SECRET="your-secret-key-change-in-production"
PORT=3001
```

View File

@ -1,23 +0,0 @@
package com.competition;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan({
"com.competition.modules.sys.mapper",
"com.competition.modules.biz.contest.mapper",
"com.competition.modules.biz.review.mapper",
"com.competition.modules.biz.homework.mapper",
"com.competition.modules.biz.judge.mapper",
"com.competition.modules.user.mapper",
"com.competition.modules.ugc.mapper",
"com.competition.modules.leai.mapper"
})
public class CompetitionApplication {
public static void main(String[] args) {
SpringApplication.run(CompetitionApplication.class, args);
}
}

View File

@ -1,11 +0,0 @@
package com.competition.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.modules.biz.contest.entity.BizContestAttachment;
import com.competition.modules.biz.contest.mapper.ContestAttachmentMapper;
import com.competition.modules.biz.contest.service.IContestAttachmentService;
import org.springframework.stereotype.Service;
@Service
public class ContestAttachmentServiceImpl extends ServiceImpl<ContestAttachmentMapper, BizContestAttachment> implements IContestAttachmentService {
}

406
docs/api/device-api.md Normal file
View File

@ -0,0 +1,406 @@
# 终端设备接口文档
> **基础地址**: `http://{服务器IP}:8580/api`
> **认证方式**: Bearer TokenJWT
> **数据格式**: JSON
> **字符编码**: UTF-8
---
## 目录
1. [发送短信验证码](#1-发送短信验证码)
2. [手机验证码登录](#2-手机验证码登录)
3. [用户作品列表](#3-用户作品列表)
4. [作品详情](#4-作品详情)
5. [通用错误码](#5-通用错误码)
---
## 接口详情
### 1. 发送短信验证码
向指定手机号发送6位数字验证码验证码有效期 **5分钟**
```
POST /device/auth/sms/send
```
**请求头**
| 参数 | 值 |
|------|------|
| Content-Type | application/json |
**请求参数Body**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| phone | string | 是 | 手机号11位 |
**请求示例**
```json
{
"phone": "13800138000"
}
```
**响应参数**
| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码200=成功 |
| message | string | 描述信息 |
| data | null | 无数据返回 |
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": null,
"timestamp": "2026-04-11T12:00:00",
"path": "/api/device/auth/sms/send"
}
```
**注意事项**
- 同一手机号 **60秒内** 只能发送一次
- 同一手机号每天最多发送 **15次**
- 验证码 **5分钟** 内有效,仅可使用一次
---
### 2. 手机验证码登录
使用手机号 + 短信验证码登录,成功返回 JWT Token 和用户信息。
```
POST /device/auth/login/sms
```
**请求头**
| 参数 | 值 |
|------|------|
| Content-Type | application/json |
**请求参数Body**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| phone | string | 是 | 手机号11位 |
| smsCode | string | 是 | 6位数字验证码 |
**请求示例**
```json
{
"phone": "13800138000",
"smsCode": "406436"
}
```
**响应参数**
| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码200=成功 |
| message | string | 描述信息 |
| data | object | 登录数据 |
| data.token | string | JWT Token后续接口需携带 |
| data.userId | long | 用户ID |
| data.username | string | 用户名 |
| data.nickname | string | 昵称 |
| data.avatar | string | 头像URL可为 null |
| data.phone | string | 手机号(脱敏,如 138****8000 |
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"token": "eyJhbGciOiJIUzM4NCJ9.eyJ0ZW5hbnRJZCI6OCwidXNlcm5hbWUiOiJkZW1vIiwic3ViIjoiOSIsImlhdCI6MTc3NTg4MjgwNywiZXhwIjoxNzc2NDg3NjA3fQ.xxx",
"userId": 9,
"username": "demo",
"nickname": "小明",
"avatar": "https://example.com/avatar.png",
"phone": "137****9302"
},
"timestamp": "2026-04-11T12:00:00",
"path": "/api/device/auth/login/sms"
}
```
**错误情况**
| code | 说明 |
|------|------|
| 404 | 该手机号未注册 |
| 403 | 账号已被禁用 |
| 400 | 验证码错误 |
| 400 | 验证码已过期 |
| 400 | 验证码不能为空 |
---
### 3. 用户作品列表
获取当前登录用户的创作作品列表(分页)。
```
GET /device/works
```
**请求头**
| 参数 | 值 | 必填 | 说明 |
|------|------|------|------|
| Authorization | Bearer {token} | 是 | 登录接口返回的 Token |
**请求参数Query**
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| page | int | 否 | 1 | 页码从1开始 |
| pageSize | int | 否 | 10 | 每页条数 |
| status | string | 否 | - | 筛选状态:`draft`/`unpublished`/`pending_review`/`published`/`rejected` |
| keyword | string | 否 | - | 标题关键词搜索 |
**请求示例**
```
GET /api/device/works?page=1&pageSize=10&status=unpublished&keyword=测试
```
**响应参数**
| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码200=成功 |
| data | object | 分页数据 |
| data.list | array | 作品列表 |
| data.total | long | 总记录数 |
| data.page | long | 当前页码 |
| data.pageSize | long | 每页条数 |
**list 数组项字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | long | 作品ID |
| title | string | 作品标题 |
| coverUrl | string | 封面图URL可为 null |
| description | string | 作品描述,可为 null |
| status | string | 状态:`draft`(草稿)/ `unpublished`(待发布)/ `pending_review`(审核中)/ `published`(已发布)/ `rejected`(已拒绝) |
| authorName | string | 作者名称 |
| leaiStatus | int | AI创作进度`-1`=失败, `0`=初始化, `1`=排队中, `2`=处理中, `3`=完成, `4`=已编目, `5`=已配音 |
| pageCount | int | 绘本页数,可为 null |
| createTime | string | 创建时间,格式 `yyyy-MM-ddTHH:mm:ss` |
| modifyTime | string | 修改时间,格式 `yyyy-MM-ddTHH:mm:ss` |
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"list": [
{
"id": 38,
"title": "我的绘本",
"coverUrl": "https://oss.example.com/cover.png",
"description": "一个有趣的故事",
"status": "unpublished",
"authorName": "小明",
"leaiStatus": 5,
"pageCount": 6,
"createTime": "2026-04-10T17:07:00",
"modifyTime": "2026-04-10T17:10:53"
},
{
"id": 37,
"title": "春天的故事",
"coverUrl": "https://oss.example.com/cover2.png",
"description": null,
"status": "published",
"authorName": "小红",
"leaiStatus": 5,
"pageCount": 6,
"createTime": "2026-04-10T16:53:17",
"modifyTime": "2026-04-10T16:58:54"
}
],
"total": 15,
"page": 1,
"pageSize": 10
}
}
```
---
### 4. 作品详情
获取指定作品的完整信息,包含所有页面内容(图片、文字、音频)。
```
GET /device/works/{id}
```
**请求头**
| 参数 | 值 | 必填 | 说明 |
|------|------|------|------|
| Authorization | Bearer {token} | 是 | 登录接口返回的 Token |
**路径参数**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | long | 是 | 作品ID |
**请求示例**
```
GET /api/device/works/38
```
**响应参数**
| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码200=成功 |
| data | object | 作品详情 |
| data.id | long | 作品ID |
| data.title | string | 作品标题 |
| data.authorName | string | 作者名称 |
| data.coverUrl | string | 封面图URL |
| data.description | string | 作品描述,可为 null |
| data.status | string | 状态 |
| data.visibility | string | 可见范围:`public`/`designated`/`internal`/`private` |
| data.createTime | string | 创建时间 |
| data.modifyTime | string | 修改时间 |
| data.pages | array | 页面列表,按页码升序排列 |
**pages 数组项字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| pageNo | int | 页码从1开始 |
| imageUrl | string | 页面图片URL |
| text | string | 页面文字内容,可为 null |
| audioUrl | string | 页面音频URL可为 null |
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"id": 38,
"title": "我的绘本",
"authorName": "小明",
"coverUrl": "https://oss.example.com/cover.png",
"description": "一个有趣的故事",
"status": "unpublished",
"visibility": "private",
"createTime": "2026-04-10T17:07:00",
"modifyTime": "2026-04-10T17:10:53",
"pages": [
{
"pageNo": 1,
"imageUrl": "https://oss.example.com/page_0.png",
"text": "从前有一座山",
"audioUrl": "https://oss.example.com/page_0.mp3"
},
{
"pageNo": 2,
"imageUrl": "https://oss.example.com/page_1.png",
"text": "山里有一座庙",
"audioUrl": "https://oss.example.com/page_1.mp3"
},
{
"pageNo": 3,
"imageUrl": "https://oss.example.com/page_2.png",
"text": "庙里有一个老和尚",
"audioUrl": null
}
]
}
}
```
**错误情况**
| code | 说明 |
|------|------|
| 404 | 作品不存在 |
| 403 | 无权访问该作品(非本人作品) |
---
## 5. 通用错误码
所有接口统一响应格式:
```json
{
"code": 401,
"message": "未登录或 Token 已过期",
"timestamp": "2026-04-11T12:00:00",
"path": "/api/device/works"
}
```
| code | 说明 |
|------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未登录或 Token 已过期 |
| 403 | 无权限访问 |
| 404 | 资源不存在 |
| 429 | 请求过于频繁(触发限流) |
| 500 | 服务器内部错误 |
---
## 调用流程
```
┌─────────────────────────────────────────────────────────┐
│ 终端设备调用流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 发送验证码 POST /device/auth/sms/send │
│ └─ 参数: { phone } │
│ │
│ 2. 验证码登录 POST /device/auth/login/sms │
│ └─ 参数: { phone, smsCode } │
│ └─ 返回: token ← 保存到本地 │
│ │
│ 3. 作品列表 GET /device/works │
│ └─ Header: Authorization: Bearer {token} │
│ └─ 参数: page, pageSize, status(可选), keyword(可选) │
│ │
│ 4. 作品详情 GET /device/works/{id} │
│ └─ Header: Authorization: Bearer {token} │
│ └─ 返回: 完整页面数据(图片+文字+音频) │
│ │
└─────────────────────────────────────────────────────────┘
```
**Token 有效期**: 7天604800秒过期后需重新登录获取。
**建议**:
- Token 存储在设备本地,每次启动时检查是否过期
- 过期后重新走 发送验证码 → 登录 流程
- 音频和图片资源使用返回的 URL 直接访问 OSS无需经过本服务

View File

@ -66,7 +66,7 @@ DATABASE_URL="mysql://用户名:密码@数据库地址:端口/数据库名"
示例(腾讯云数据库):
```env
DATABASE_URL="mysql://root:password@gz-cdb-xxx.sql.tencentcdb.com:20704/db_competition_management"
DATABASE_URL="mysql://root:password@gz-cdb-xxx.sql.tencentcdb.com:20704/db_lesingle-creation-test"
```
### 3. 生成 Prisma Client
@ -214,7 +214,7 @@ pnpm init:menus
pnpm init:roles:all
# 导出数据
mysqldump -u root db_competition_management > init_data.sql
mysqldump -u root db_lesingle-creation-test > init_data.sql
```
**导入到云数据库**(使用 DBeaver

View File

@ -96,6 +96,6 @@
|------|-----|
| 后端 | http://localhost:3234 |
| 前端 | http://localhost:3000 |
| 数据库 | mysql://root@localhost:3306/competition_management |
| 数据库 | mysql://root@localhost:3306/lesingle-creation-test |
| 测试活动 | ID=1(公开), ID=2(仅内部), ID=3(定向广州6-12岁) |
| 测试用户 | testuser01(广州,有子女), beijing_user(北京), demo(默认) |

View File

@ -1 +0,0 @@
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/ai-3d.ts","./src/api/analytics.ts","./src/api/auth.ts","./src/api/classes.ts","./src/api/config.ts","./src/api/contests.ts","./src/api/departments.ts","./src/api/dict.ts","./src/api/grades.ts","./src/api/homework.ts","./src/api/judges-management.ts","./src/api/logs.ts","./src/api/menus.ts","./src/api/permissions.ts","./src/api/preset-comments.ts","./src/api/public.ts","./src/api/roles.ts","./src/api/students.ts","./src/api/teachers.ts","./src/api/tenants.ts","./src/api/upload.ts","./src/api/users.ts","./src/api/aicreate/index.ts","./src/api/aicreate/types.ts","./src/composables/usecontestworkpreviewpages.ts","./src/composables/uselistrequest.ts","./src/composables/usesimplelistrequest.ts","./src/directives/permission.ts","./src/router/index.ts","./src/stores/aicreate.ts","./src/stores/auth.ts","./src/types/api.ts","./src/types/auth.ts","./src/types/router.ts","./src/utils/auth.ts","./src/utils/avatar.ts","./src/utils/menu.ts","./src/utils/oss-env.ts","./src/utils/request.ts","./src/utils/sanitize.ts","./src/utils/aicreate/config.ts","./src/utils/aicreate/hmac.ts","./src/utils/aicreate/status.ts","./src/app.vue","./src/components/modelviewer.vue","./src/components/richtexteditor.vue","./src/components/aicreate/pageheader.vue","./src/components/aicreate/stepbar.vue","./src/layouts/basiclayout.vue","./src/layouts/emptylayout.vue","./src/layouts/publiclayout.vue","./src/views/activities/comments.vue","./src/views/activities/guidance.vue","./src/views/activities/presetcomments.vue","./src/views/activities/review.vue","./src/views/activities/reviewdetail.vue","./src/views/activities/components/reviewworkmodal.vue","./src/views/analytics/overview.vue","./src/views/analytics/review.vue","./src/views/auth/login.vue","./src/views/content/tagmanagement.vue","./src/views/content/workmanagement.vue","./src/views/content/workreview.vue","./src/views/contests/activities.vue","./src/views/contests/create.vue","./src/views/contests/detail.vue","./src/views/contests/guidance.vue","./src/views/contests/index.vue","./src/views/contests/registerindividual.vue","./src/views/contests/registerteam.vue","./src/views/contests/superdetail.vue","./src/views/contests/components/addjudgedrawer.vue","./src/views/contests/components/addparticipantdrawer.vue","./src/views/contests/components/addteacherdrawer.vue","./src/views/contests/components/submitworkdrawer.vue","./src/views/contests/components/viewworkdrawer.vue","./src/views/contests/components/workdetailmodal.vue","./src/views/contests/judges/index.vue","./src/views/contests/notices/index.vue","./src/views/contests/registrations/index.vue","./src/views/contests/registrations/records.vue","./src/views/contests/results/detail.vue","./src/views/contests/results/index.vue","./src/views/contests/reviews/index.vue","./src/views/contests/reviews/progress.vue","./src/views/contests/reviews/progressdetail.vue","./src/views/contests/reviews/tasks.vue","./src/views/contests/works/index.vue","./src/views/contests/works/worksdetail.vue","./src/views/error/403.vue","./src/views/error/404.vue","./src/views/homework/index.vue","./src/views/homework/reviewrules.vue","./src/views/homework/studentdetail.vue","./src/views/homework/studentlist.vue","./src/views/homework/submissions.vue","./src/views/public/activities.vue","./src/views/public/activitydetail.vue","./src/views/public/gallery.vue","./src/views/public/login.vue","./src/views/public/components/workselector.vue","./src/views/public/create/index.vue","./src/views/public/create/views/bookreaderview.vue","./src/views/public/create/views/charactersview.vue","./src/views/public/create/views/creatingview.vue","./src/views/public/create/views/dubbingview.vue","./src/views/public/create/views/editinfoview.vue","./src/views/public/create/views/previewview.vue","./src/views/public/create/views/savesuccessview.vue","./src/views/public/create/views/storyinputview.vue","./src/views/public/create/views/styleselectview.vue","./src/views/public/create/views/uploadview.vue","./src/views/public/create/views/welcomeview.vue","./src/views/public/mine/children.vue","./src/views/public/mine/favorites.vue","./src/views/public/mine/index.vue","./src/views/public/mine/registrations.vue","./src/views/public/works/detail.vue","./src/views/public/works/index.vue","./src/views/public/works/publish.vue","./src/views/system/config/index.vue","./src/views/system/dict/index.vue","./src/views/system/logs/index.vue","./src/views/system/menus/index.vue","./src/views/system/permissions/index.vue","./src/views/system/public-users/index.vue","./src/views/system/roles/index.vue","./src/views/system/tenant-info/index.vue","./src/views/system/tenants/index.vue","./src/views/system/users/index.vue","./src/views/workbench/index.vue","./src/views/workbench/tenantdashboard.vue"],"errors":true,"version":"5.9.3"}

View File

@ -1,2 +0,0 @@
declare const _default: import("vite").UserConfigFnObject;
export default _default;

View File

@ -1,38 +0,0 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
// 根据环境设置 base 路径
var getBase = function (mode) {
switch (mode) {
case "test":
return "/web-test/";
case "production":
return "/web/";
default:
return "/";
}
};
// https://vitejs.dev/config/
export default defineConfig(function (_a) {
var mode = _a.mode;
return {
base: getBase(mode),
plugins: [vue()],
resolve: {
alias: {
"@": resolve(__dirname, "src"),
},
},
server: {
host: "0.0.0.0",
port: 3000,
proxy: {
// 主后端
"/api": {
target: "http://localhost:8580",
changeOrigin: true,
},
},
},
};
});

View File

@ -0,0 +1,383 @@
# 企业同步创作数据 — 核心三步
> 基于《AI绘本创作系统 企业后端集成指南 V4.0》,提炼企业同步乐读派创作数据的核心实现路径。
>
> 三层防线共用同一套同步判断规则,区别仅在于触发方式和时机。
---
## 前置知识5步状态机与同步判断规则
### 状态定义
| 数值 | 状态名 | 含义 |
|------|--------|------|
| -1 | FAILED | 创作失败(异常状态) |
| 1 | PENDING | 已提交,排队等待 |
| 2 | PROCESSING | AI创作中进度变化 |
| 3 | COMPLETED | 图片完成,待编目 |
| 4 | CATALOGED | 编目完成,待配音 |
| 5 | DUBBED | 配音完成(乐读派终态) |
正常流转:`1 → 2 → 3 → 4 → 5`,严格单向递增,不可回退。
### 统一同步判断规则(三步通用)
```
if (本地无记录) → INSERT新作品
if (remote_status == -1) → 强制UPDATE失败通知无条件处理
if (remote_status == 2) → 强制UPDATE进度变化无条件更新
if (remote_status > local_status) → 全量UPDATE状态前进
其他 → SKIP旧数据/重复,忽略)
```
> **重点**PROCESSING(2) 和 FAILED(-1) 是特殊状态,**不参与数值大小比较**,收到即强制更新。
---
## 第一步Webhook 回调推送(实时主通道)
### 作用
乐读派后端在作品状态变更时,**主动** POST 推送到企业配置的 Webhook URL实现秒级数据同步。
### 触发时机
每次 status 发生变化时自动推送,包括:
- 状态前进1→2、2→3、3→4、4→5
- 创作失败any→-1
### 两种事件类型
| 事件 | 触发时机 | 频率 | 说明 |
|------|----------|------|------|
| `work.status_changed` | 状态变更 | 每次状态变更1次 | 核心事件,携带完整作品数据(含 page_list |
| `work.progress` | PROCESSING 阶段进度变化 | 多次/作品 | 进度里程碑推送10%/30%/50%/70%/90% |
### Webhook 请求格式
```
POST {企业webhook_url}
Content-Type: application/json
X-Webhook-Id: evt_190368671438289
X-Webhook-Event: work.status_changed
X-Webhook-Timestamp: 1712000000000
X-Webhook-Signature: HMAC-SHA256=a3f8c2d1...
```
### 企业处理逻辑Java 伪代码)
```java
@Transactional
public void handleWebhook(JSONObject data) {
String workId = data.getString("work_id");
int remoteStatus = data.getIntValue("status");
// 1. 幂等去重(用 event_id防止重复处理
if (processedEvents.contains(eventId)) return;
// 2. 查本地记录(建议 SELECT ... FOR UPDATE 行锁)
WorkRecord local = db.selectForUpdate(workId);
// 3. 新作品 → 直接入库
if (local == null) {
db.insert(buildRecord(data));
return;
}
// 4. FAILED(-1) → 强制更新,无条件
if (remoteStatus == -1) {
local.setStatus(-1);
local.setFailReason(data.getString("fail_reason"));
db.update(local);
return;
}
// 5. PROCESSING(2) → 强制更新进度,无条件
if (remoteStatus == 2) {
local.setProgress(data.getIntValue("progress"));
local.setProgressMessage(data.getString("progress_message"));
db.update(local);
return;
}
// 6. 状态前进 → 全量覆盖
if (remoteStatus > local.getStatus()) {
updateAllFields(local, data); // 更新 title/author/page_list 等
db.update(local);
return;
}
// 7. 旧数据/重复 → 忽略
log.info("skip: remote={} <= local={}", remoteStatus, local.getStatus());
}
```
### 各状态下企业应保存的关键数据
| 状态 | 企业需保存的字段 | 用途 |
|------|--------------|------|
| 1 PENDING | work_id, phone, org_id, style, original_image_url | 创建本地记录,关联企业用户 |
| 2 PROCESSING | progress, progress_message | 显示创作进度(可选) |
| 3 COMPLETED | title, pages, page_list含 image_url | 作品图片已生成,可预览展示 |
| 4 CATALOGED | title, author, subtitle, intro, tags | 用户填写的编目信息 |
| 5 DUBBED | page_list含 audio_url | 配音URL已填充作品完整可用 |
| -1 FAILED | fail_reason | 记录失败原因,通知用户 |
### 签名验证(必须实现)
```
签名体 = "{X-Webhook-Id}.{X-Webhook-Timestamp}.{请求body原文}"
期望签名 = HMAC-SHA256(签名体, app_secret).toHex()
```
安全检查清单:
- **时间窗口**`|当前时间 - X-Webhook-Timestamp| ≤ 5分钟`,防重放
- **幂等去重**:用 `X-Webhook-Id` 记录已处理事件
- **常量时间比较**:用 `MessageDigest.isEqual()` 而非 `.equals()`,防时序攻击
- **原始body**:签名时使用 HTTP body 原文,不要反序列化再序列化
### 重试策略
首次发送 + 5次重试 = 共6次尝试。
延迟间隔:`10s → 30s → 2min → 10min → 30min → 30min`
全部失败后需通过**第二步 B3 批量拉取**兜底。
---
## 第二步B3 批量查询兜底(定时对账)
### 作用
Webhook 可能因网络问题、企业服务宕机等原因丢失。B3 定时批量拉取作为兜底通道,确保数据**最终一致**。
### 接口信息
```
GET /api/v1/query/works?orgId=ORG001&updatedAfter=2026-04-05T00:00:00
认证方式HMAC-SHA256 签名
```
返回指定时间之后有变更的所有作品列表。
### 建议配置
| 配置项 | 推荐值 | 说明 |
|--------|--------|------|
| 对账频率 | 每 30 分钟 | 不低于 15 分钟,避免对 API 造成查询压力 |
| 查询范围 | 最近 2 小时 | 覆盖 2 个对账周期,防止边界遗漏 |
### 企业实现伪代码Java
```java
@Scheduled(fixedRate = 30 * 60 * 1000) // 每30分钟执行
public void reconcile() {
// 查询最近2小时内有变更的作品
String since = twoHoursAgo.format(ISO_FORMAT);
List<RemoteWork> remoteList = callB3(since);
for (RemoteWork remote : remoteList) {
WorkRecord local = db.get(remote.workId);
// 同步判断规则与 Webhook 完全一致
if (local == null
|| remote.status > local.getStatus()
|| remote.status == 2
|| remote.status == -1) {
db.upsert(remote);
}
}
lastSyncTime = now();
}
```
### B3 调用签名示例
```java
Map<String, String> params = new LinkedHashMap<>();
params.put("orgId", ORG_ID);
params.put("updatedAfter", "2026-04-05T00:00:00");
// 生成 HMAC 签名头4个 Header
Map<String, String> headers = buildHmacHeaders(params, ORG_ID, APP_SECRET);
String url = API_URL + "/api/v1/query/works?orgId=" + ORG_ID
+ "&updatedAfter=" + URLEncoder.encode("2026-04-05T00:00:00", "UTF-8");
// 注意:传 URI 对象,避免 RestTemplate 双重编码
restTemplate.getForObject(URI.create(url), String.class);
```
### 并发安全Webhook + B3 同时触发)
推荐两种方案防止同一作品被并发更新:
```sql
-- 方案1: 行锁(推荐,简单可靠)
SELECT * FROM enterprise_work WHERE work_id = ? FOR UPDATE;
-- 然后按同步规则判断和更新
-- 方案2: CAS 乐观锁(无锁,适合高并发)
UPDATE enterprise_work
SET status = #{remoteStatus}, title = #{title}, page_list = #{pageList}
WHERE work_id = #{workId}
AND (status < #{remoteStatus} OR #{remoteStatus} = 2 OR #{remoteStatus} = -1);
-- rows=0 表示被其他线程抢先更新了,安全忽略
```
---
## 第三步:详情页进入时强制 B2 拉取(用户触发兜底)
### 作用
当用户点击进入作品详情页,且本地状态尚未完成(`status < 3`企业**强制调用 B2 单条查询**拉取最新数据确保用户看到的是最新状态
### 为什么需要这一步
- status 1/2 期间状态变化最快(排队 → 创作中 → 完成),是 Webhook 丢失的**高风险窗口**
- B3 对账有 30 分钟延迟,用户可能在此期间打开详情页
- 用户主动触发,**即时补偿**,体验最佳
### 触发条件
```
用户点击作品详情页 && local_status < 3
```
`status >= 3` 后变化由用户主动操作驱动(编目/配音Webhook + B3 已足够覆盖,无需额外拉取。
### 接口信息
```
GET /api/v1/query/work/{workId}?orgId=ORG001
认证方式HMAC-SHA256 签名 或 Session Token
```
### 企业实现伪代码Java
```java
/**
* 用户点击作品详情页时调用
*/
public WorkRecord getWorkDetail(String workId) {
WorkRecord local = db.get(workId);
// 本地状态 < 3PENDING PROCESSING强制从乐读派拉取最新
if (local != null && local.getStatus() < 3) {
RemoteWork remote = callB2(workId);
// 同步判断规则与 Webhook、B3 完全一致
if (remote.status > local.getStatus()
|| remote.status == 2
|| remote.status == -1) {
updateAllFields(local, remote);
db.update(local);
}
}
// 返回最新的本地记录,渲染详情页
return db.get(workId);
}
```
### 前端配合(可选)
如果企业有自建 C 端,可在作品列表页面根据 status 做路由跳转:
```javascript
switch (work.status) {
case 1: case 2: // 排队/创作中
navigate('/creating/' + workId); // → 进度等待页
break;
case 3: // 图片完成
navigate('/catalog/' + workId); // → 编目编辑页(强制)
break;
case 4: // 编目完成
navigate('/dubbing/' + workId); // → 配音编辑页(强制)
break;
case 5: // 配音完成(终态)
navigate('/reader/' + workId); // → 阅读页
break;
case -1: // 失败
showError(work.failReason);
break;
}
```
---
## 三步协同总览
```
┌──────────────────────────────────────────────────────────────────┐
│ 企业同步三层防线 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 第一步 Webhook 推送 秒级实时 ← 主通道(可能丢失) │
│ ↓ 漏网之鱼 │
│ 第二步 B3 定时对账 30分钟兜底 ← 保障通道(高可靠) │
│ ↓ 用户等不及 │
│ 第三步 B2 详情页强制拉取 用户触发 ← 即时补偿status<3时
│ │
├──────────────────────────────────────────────────────────────────┤
│ 三层共用统一同步规则: │
│ · remote > local → 全量更新 │
│ · remote == 2 (进度) → 强制更新 │
│ · remote == -1 (失败) → 强制更新 │
│ · 其他 → 忽略 │
└──────────────────────────────────────────────────────────────────┘
```
### 数据流对比
| 维度 | 第一步 Webhook | 第二步 B3 对账 | 第三步 B2 强制拉取 |
|------|-------------|------------|---------------|
| 触发方式 | 乐读派主动推送 | 企业定时轮询 | 用户打开详情页 |
| 实时性 | 秒级 | 30分钟级 | 即时 |
| 可靠性 | 可能丢失 | 高可靠 | 高可靠 |
| 触发条件 | 状态变更时自动 | 定时任务 | `local_status < 3` |
| 数据范围 | 单条作品 | 时间范围内批量 | 单条作品 |
| 认证方式 | 签名验证(被动接收) | HMAC 签名(主动请求) | HMAC/Token主动请求 |
---
## 企业推荐表结构
```sql
CREATE TABLE `enterprise_work` (
`work_id` VARCHAR(32) PRIMARY KEY,
`status` INT NOT NULL DEFAULT 0 COMMENT '状态: 乐读派1-5, 企业>=6',
`progress` INT DEFAULT 0 COMMENT '创作进度0-100',
`title` VARCHAR(200),
`author` VARCHAR(50),
`tags` JSON,
`page_list` JSON COMMENT '页面数据含image_url/audio_url',
`original_image_url` VARCHAR(500) COMMENT '用户原创作品图片URL',
`phone` VARCHAR(20) COMMENT '用户手机号(关联企业用户)',
`fail_reason` VARCHAR(500),
`webhook_event_id` VARCHAR(64) COMMENT '最近一次webhook事件ID幂等去重',
`synced_at` DATETIME COMMENT '最近同步时间',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_status` (`status`),
INDEX `idx_phone` (`phone`)
);
```
---
## 管理后台配置项
企业需在乐读派管理后台「机构管理」中配置以下 2 项:
| 配置项 | 填写内容 | 说明 |
|--------|--------|------|
| Webhook URL | `https://你的域名/webhook/leai` | 接收作品状态变更推送(第一步) |
| 认证回调URL | `https://你的域名/leai-auth` | H5 token 失效后跳回重新认证 |
---
> 完整接口规范、签名算法、代码示例请参考《AI绘本创作系统 企业后端集成指南 V4.0》及随附的 `enterprise-demo/java-demo/` 目录。

View File

@ -11,10 +11,10 @@
<relativePath/>
</parent>
<groupId>com.competition</groupId>
<artifactId>competition-management-system</artifactId>
<groupId>com.lesingle</groupId>
<artifactId>lesingle-creation-backend</artifactId>
<version>1.0.0</version>
<name>competition-management-system</name>
<name>lesingle-creation-backend</name>
<description>少儿绘本创作活动管理平台 - Java 后端</description>
<properties>
@ -161,6 +161,7 @@
</dependencies>
<build>
<finalName>lesingle-creation-backend</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@ -0,0 +1,23 @@
package com.lesingle;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan({
"com.lesingle.modules.sys.mapper",
"com.lesingle.modules.biz.contest.mapper",
"com.lesingle.modules.biz.review.mapper",
"com.lesingle.modules.biz.homework.mapper",
"com.lesingle.modules.biz.judge.mapper",
"com.lesingle.modules.user.mapper",
"com.lesingle.modules.ugc.mapper",
"com.lesingle.modules.leai.mapper"
})
public class LesingleCreationApplication {
public static void main(String[] args) {
SpringApplication.run(LesingleCreationApplication.class, args);
}
}

View File

@ -1,4 +1,4 @@
package com.competition.common.annotation;
package com.lesingle.common.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@ -1,4 +1,4 @@
package com.competition.common.config;
package com.lesingle.common.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

View File

@ -1,7 +1,7 @@
package com.competition.common.config;
package com.lesingle.common.config;
import com.competition.common.interceptor.RateLimitInterceptor;
import com.competition.common.interceptor.TraceIdInterceptor;
import com.lesingle.common.interceptor.RateLimitInterceptor;
import com.lesingle.common.interceptor.TraceIdInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -22,6 +22,6 @@ public class WebMvcConfig implements WebMvcConfigurer {
registry.addInterceptor(traceIdInterceptor).addPathPatterns("/**");
// 速率限制拦截器仅对公开接口生效
registry.addInterceptor(rateLimitInterceptor)
.addPathPatterns("/public/**", "/webhook/**");
.addPathPatterns("/public/**", "/webhook/**", "/device/**");
}
}

View File

@ -1,4 +1,4 @@
package com.competition.common.constants;
package com.lesingle.common.constants;
/**
* BaseEntity 基础字段常量

View File

@ -1,4 +1,4 @@
package com.competition.common.constants;
package com.lesingle.common.constants;
/**
* 缓存相关常量

View File

@ -1,4 +1,4 @@
package com.competition.common.constants;
package com.lesingle.common.constants;
/**
* 角色相关常量

View File

@ -1,4 +1,4 @@
package com.competition.common.constants;
package com.lesingle.common.constants;
import java.util.Set;

View File

@ -1,4 +1,4 @@
package com.competition.common.entity;
package com.lesingle.common.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.competition.common.enums;
package com.lesingle.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@ -1,6 +1,6 @@
package com.competition.common.exception;
package com.lesingle.common.exception;
import com.competition.common.enums.ErrorCode;
import com.lesingle.common.enums.ErrorCode;
import lombok.Getter;
/**

View File

@ -1,7 +1,7 @@
package com.competition.common.exception;
package com.lesingle.common.exception;
import com.competition.common.enums.ErrorCode;
import com.competition.common.result.Result;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.result.Result;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;

View File

@ -1,7 +1,7 @@
package com.competition.common.handler;
package com.lesingle.common.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.competition.common.util.SecurityUtil;
import com.lesingle.common.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

View File

@ -1,7 +1,7 @@
package com.competition.common.interceptor;
package com.lesingle.common.interceptor;
import com.competition.common.annotation.RateLimit;
import com.competition.common.result.Result;
import com.lesingle.common.annotation.RateLimit;
import com.lesingle.common.result.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

View File

@ -1,4 +1,4 @@
package com.competition.common.interceptor;
package com.lesingle.common.interceptor;
import cn.hutool.core.util.IdUtil;
import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,4 +1,4 @@
package com.competition.common.result;
package com.lesingle.common.result;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;

View File

@ -1,6 +1,6 @@
package com.competition.common.result;
package com.lesingle.common.result;
import com.competition.common.enums.ErrorCode;
import com.lesingle.common.enums.ErrorCode;
import lombok.Data;
import java.io.Serializable;

View File

@ -1,6 +1,6 @@
package com.competition.common.util;
package com.lesingle.common.util;
import com.competition.security.model.LoginUser;
import com.lesingle.security.model.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

View File

@ -1,4 +1,4 @@
package com.competition.common.util;
package com.lesingle.common.util;
/**
* 敏感信息脱敏工具类

View File

@ -1,10 +1,10 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.competition.common.result.Result;
import com.competition.modules.biz.contest.entity.BizContestAttachment;
import com.competition.modules.biz.contest.service.IContestAttachmentService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.Result;
import com.lesingle.modules.biz.contest.entity.BizContestAttachment;
import com.lesingle.modules.biz.contest.service.IContestAttachmentService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

View File

@ -1,14 +1,14 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.CreateContestDto;
import com.competition.modules.biz.contest.dto.QueryContestDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.service.IContestService;
import com.competition.modules.sys.service.ISysTenantService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.CreateContestDto;
import com.lesingle.modules.biz.contest.dto.QueryContestDto;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.service.IContestService;
import com.lesingle.modules.sys.service.ISysTenantService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,18 +1,18 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.competition.common.enums.ErrorCode;
import com.competition.common.enums.PublishStatus;
import com.competition.common.exception.BusinessException;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.CreateNoticeDto;
import com.competition.modules.biz.contest.entity.BizContestNotice;
import com.competition.modules.biz.contest.service.IContestNoticeService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.enums.PublishStatus;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.CreateNoticeDto;
import com.lesingle.modules.biz.contest.entity.BizContestNotice;
import com.lesingle.modules.biz.contest.service.IContestNoticeService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,12 +1,12 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
import com.competition.modules.biz.contest.service.IContestRegistrationService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.CreateRegistrationDto;
import com.lesingle.modules.biz.contest.dto.QueryRegistrationDto;
import com.lesingle.modules.biz.contest.service.IContestRegistrationService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,11 +1,11 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.CreateTeamDto;
import com.competition.modules.biz.contest.entity.BizContestTeam;
import com.competition.modules.biz.contest.service.IContestTeamService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.CreateTeamDto;
import com.lesingle.modules.biz.contest.entity.BizContestTeam;
import com.lesingle.modules.biz.contest.service.IContestTeamService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,12 +1,12 @@
package com.competition.modules.biz.contest.controller;
package com.lesingle.modules.biz.contest.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.QueryWorkDto;
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
import com.competition.modules.biz.contest.service.IContestWorkService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.QueryWorkDto;
import com.lesingle.modules.biz.contest.dto.SubmitWorkDto;
import com.lesingle.modules.biz.contest.service.IContestWorkService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.dto;
package com.lesingle.modules.biz.contest.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,9 +1,9 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,8 +1,8 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,8 +1,8 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,8 +1,8 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,8 +1,8 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,9 +1,9 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.competition.common.entity.BaseEntity;
import com.lesingle.common.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.contest.entity;
package com.lesingle.modules.biz.contest.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestAttachment;
import com.lesingle.modules.biz.contest.entity.BizContestAttachment;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContest;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestNotice;
import com.lesingle.modules.biz.contest.entity.BizContestNotice;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestRegistrationTeacher;
import com.lesingle.modules.biz.contest.entity.BizContestRegistrationTeacher;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestTeam;
import com.lesingle.modules.biz.contest.entity.BizContestTeam;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestTeamMember;
import com.lesingle.modules.biz.contest.entity.BizContestTeamMember;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestWorkAttachment;
import com.lesingle.modules.biz.contest.entity.BizContestWorkAttachment;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.mapper;
package com.lesingle.modules.biz.contest.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.competition.modules.biz.contest.entity.BizContestWork;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import org.apache.ibatis.annotations.Mapper;
@Mapper

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.modules.biz.contest.entity.BizContestAttachment;
import com.lesingle.modules.biz.contest.entity.BizContestAttachment;
public interface IContestAttachmentService extends IService<BizContestAttachment> {
}

View File

@ -1,7 +1,7 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.modules.biz.contest.entity.BizContestNotice;
import com.lesingle.modules.biz.contest.entity.BizContestNotice;
import java.util.List;

View File

@ -1,10 +1,10 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.common.result.PageResult;
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
import com.competition.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.dto.CreateRegistrationDto;
import com.lesingle.modules.biz.contest.dto.QueryRegistrationDto;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import java.util.List;
import java.util.Map;

View File

@ -1,10 +1,10 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.common.result.PageResult;
import com.competition.modules.biz.contest.dto.CreateContestDto;
import com.competition.modules.biz.contest.dto.QueryContestDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.dto.CreateContestDto;
import com.lesingle.modules.biz.contest.dto.QueryContestDto;
import com.lesingle.modules.biz.contest.entity.BizContest;
import java.util.Map;

View File

@ -1,8 +1,8 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.modules.biz.contest.dto.CreateTeamDto;
import com.competition.modules.biz.contest.entity.BizContestTeam;
import com.lesingle.modules.biz.contest.dto.CreateTeamDto;
import com.lesingle.modules.biz.contest.entity.BizContestTeam;
import java.util.List;
import java.util.Map;

View File

@ -1,10 +1,10 @@
package com.competition.modules.biz.contest.service;
package com.lesingle.modules.biz.contest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.competition.common.result.PageResult;
import com.competition.modules.biz.contest.dto.QueryWorkDto;
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
import com.competition.modules.biz.contest.entity.BizContestWork;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.dto.QueryWorkDto;
import com.lesingle.modules.biz.contest.dto.SubmitWorkDto;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import java.util.List;
import java.util.Map;

View File

@ -0,0 +1,11 @@
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lesingle.modules.biz.contest.entity.BizContestAttachment;
import com.lesingle.modules.biz.contest.mapper.ContestAttachmentMapper;
import com.lesingle.modules.biz.contest.service.IContestAttachmentService;
import org.springframework.stereotype.Service;
@Service
public class ContestAttachmentServiceImpl extends ServiceImpl<ContestAttachmentMapper, BizContestAttachment> implements IContestAttachmentService {
}

View File

@ -1,11 +1,11 @@
package com.competition.modules.biz.contest.service.impl;
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.entity.BizContestNotice;
import com.competition.modules.biz.contest.mapper.ContestNoticeMapper;
import com.competition.modules.biz.contest.service.IContestNoticeService;
import com.competition.modules.biz.contest.service.IContestService;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContestNotice;
import com.lesingle.modules.biz.contest.mapper.ContestNoticeMapper;
import com.lesingle.modules.biz.contest.service.IContestNoticeService;
import com.lesingle.modules.biz.contest.service.IContestService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

View File

@ -1,27 +1,27 @@
package com.competition.modules.biz.contest.service.impl;
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.common.enums.ErrorCode;
import com.competition.common.enums.ParticipantType;
import com.competition.common.enums.PublishStatus;
import com.competition.common.enums.RegistrationStatus;
import com.competition.common.exception.BusinessException;
import com.competition.common.result.PageResult;
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.entity.BizContestRegistration;
import com.competition.modules.biz.contest.entity.BizContestRegistrationTeacher;
import com.competition.modules.biz.contest.mapper.ContestMapper;
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.competition.modules.biz.contest.mapper.ContestRegistrationTeacherMapper;
import com.competition.modules.biz.contest.service.IContestRegistrationService;
import com.competition.modules.sys.entity.SysTenant;
import com.competition.modules.sys.entity.SysUser;
import com.competition.modules.sys.mapper.SysTenantMapper;
import com.competition.modules.sys.mapper.SysUserMapper;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.enums.ParticipantType;
import com.lesingle.common.enums.PublishStatus;
import com.lesingle.common.enums.RegistrationStatus;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.dto.CreateRegistrationDto;
import com.lesingle.modules.biz.contest.dto.QueryRegistrationDto;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestRegistrationTeacher;
import com.lesingle.modules.biz.contest.mapper.ContestMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationTeacherMapper;
import com.lesingle.modules.biz.contest.service.IContestRegistrationService;
import com.lesingle.modules.sys.entity.SysTenant;
import com.lesingle.modules.sys.entity.SysUser;
import com.lesingle.modules.sys.mapper.SysTenantMapper;
import com.lesingle.modules.sys.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@ -1,30 +1,30 @@
package com.competition.modules.biz.contest.service.impl;
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.common.enums.ErrorCode;
import com.competition.common.enums.PublishStatus;
import com.competition.common.enums.RegistrationStatus;
import com.competition.common.enums.SubmitRule;
import com.competition.common.exception.BusinessException;
import com.competition.common.result.PageResult;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.contest.dto.CreateContestDto;
import com.competition.modules.biz.contest.dto.QueryContestDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.entity.BizContestAttachment;
import com.competition.modules.biz.contest.entity.BizContestRegistration;
import com.competition.modules.biz.contest.entity.BizContestWork;
import com.competition.modules.biz.contest.mapper.ContestAttachmentMapper;
import com.competition.modules.biz.contest.mapper.ContestMapper;
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
import com.competition.modules.biz.contest.service.IContestService;
import com.competition.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.competition.modules.sys.entity.SysTenant;
import com.competition.modules.sys.mapper.SysTenantMapper;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.enums.PublishStatus;
import com.lesingle.common.enums.RegistrationStatus;
import com.lesingle.common.enums.SubmitRule;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.contest.dto.CreateContestDto;
import com.lesingle.modules.biz.contest.dto.QueryContestDto;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContestAttachment;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import com.lesingle.modules.biz.contest.mapper.ContestAttachmentMapper;
import com.lesingle.modules.biz.contest.mapper.ContestMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.lesingle.modules.biz.contest.mapper.ContestWorkMapper;
import com.lesingle.modules.biz.contest.service.IContestService;
import com.lesingle.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.lesingle.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.lesingle.modules.sys.entity.SysTenant;
import com.lesingle.modules.sys.mapper.SysTenantMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@ -1,15 +1,15 @@
package com.competition.modules.biz.contest.service.impl;
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.common.enums.ErrorCode;
import com.competition.common.exception.BusinessException;
import com.competition.modules.biz.contest.dto.CreateTeamDto;
import com.competition.modules.biz.contest.entity.BizContestTeam;
import com.competition.modules.biz.contest.entity.BizContestTeamMember;
import com.competition.modules.biz.contest.mapper.ContestTeamMapper;
import com.competition.modules.biz.contest.mapper.ContestTeamMemberMapper;
import com.competition.modules.biz.contest.service.IContestTeamService;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.modules.biz.contest.dto.CreateTeamDto;
import com.lesingle.modules.biz.contest.entity.BizContestTeam;
import com.lesingle.modules.biz.contest.entity.BizContestTeamMember;
import com.lesingle.modules.biz.contest.mapper.ContestTeamMapper;
import com.lesingle.modules.biz.contest.mapper.ContestTeamMemberMapper;
import com.lesingle.modules.biz.contest.service.IContestTeamService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@ -1,36 +1,36 @@
package com.competition.modules.biz.contest.service.impl;
package com.lesingle.modules.biz.contest.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.common.enums.ErrorCode;
import com.competition.common.enums.WorkStatus;
import com.competition.common.enums.RegistrationStatus;
import com.competition.common.exception.BusinessException;
import com.competition.common.result.PageResult;
import com.competition.modules.biz.contest.dto.QueryWorkDto;
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.entity.BizContestRegistration;
import com.competition.modules.biz.contest.entity.BizContestWork;
import com.competition.modules.biz.contest.entity.BizContestWorkAttachment;
import com.competition.modules.biz.contest.mapper.ContestMapper;
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.competition.modules.biz.contest.mapper.ContestWorkAttachmentMapper;
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
import com.competition.modules.biz.contest.service.IContestWorkService;
import com.competition.modules.biz.review.entity.BizContestJudge;
import com.competition.modules.biz.review.entity.BizContestReviewRule;
import com.competition.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.competition.modules.biz.review.entity.BizContestWorkScore;
import com.competition.modules.biz.review.mapper.ContestJudgeMapper;
import com.competition.modules.biz.review.mapper.ContestReviewRuleMapper;
import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.competition.modules.biz.review.mapper.ContestWorkScoreMapper;
import com.competition.modules.biz.review.util.ContestFinalScoreCalculator;
import com.competition.modules.sys.entity.SysUser;
import com.competition.modules.sys.mapper.SysUserMapper;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.enums.WorkStatus;
import com.lesingle.common.enums.RegistrationStatus;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.PageResult;
import com.lesingle.modules.biz.contest.dto.QueryWorkDto;
import com.lesingle.modules.biz.contest.dto.SubmitWorkDto;
import com.lesingle.modules.biz.contest.entity.BizContest;
import com.lesingle.modules.biz.contest.entity.BizContestRegistration;
import com.lesingle.modules.biz.contest.entity.BizContestWork;
import com.lesingle.modules.biz.contest.entity.BizContestWorkAttachment;
import com.lesingle.modules.biz.contest.mapper.ContestMapper;
import com.lesingle.modules.biz.contest.mapper.ContestRegistrationMapper;
import com.lesingle.modules.biz.contest.mapper.ContestWorkAttachmentMapper;
import com.lesingle.modules.biz.contest.mapper.ContestWorkMapper;
import com.lesingle.modules.biz.contest.service.IContestWorkService;
import com.lesingle.modules.biz.review.entity.BizContestJudge;
import com.lesingle.modules.biz.review.entity.BizContestReviewRule;
import com.lesingle.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.lesingle.modules.biz.review.entity.BizContestWorkScore;
import com.lesingle.modules.biz.review.mapper.ContestJudgeMapper;
import com.lesingle.modules.biz.review.mapper.ContestReviewRuleMapper;
import com.lesingle.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.lesingle.modules.biz.review.mapper.ContestWorkScoreMapper;
import com.lesingle.modules.biz.review.util.ContestFinalScoreCalculator;
import com.lesingle.modules.sys.entity.SysUser;
import com.lesingle.modules.sys.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@ -1,12 +1,12 @@
package com.competition.modules.biz.homework.controller;
package com.lesingle.modules.biz.homework.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.homework.dto.CreateHomeworkDto;
import com.competition.modules.biz.homework.entity.BizHomework;
import com.competition.modules.biz.homework.service.IHomeworkService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.homework.dto.CreateHomeworkDto;
import com.lesingle.modules.biz.homework.entity.BizHomework;
import com.lesingle.modules.biz.homework.service.IHomeworkService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,12 +1,12 @@
package com.competition.modules.biz.homework.controller;
package com.lesingle.modules.biz.homework.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.homework.dto.CreateHomeworkReviewRuleDto;
import com.competition.modules.biz.homework.entity.BizHomeworkReviewRule;
import com.competition.modules.biz.homework.service.IHomeworkReviewRuleService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.homework.dto.CreateHomeworkReviewRuleDto;
import com.lesingle.modules.biz.homework.entity.BizHomeworkReviewRule;
import com.lesingle.modules.biz.homework.service.IHomeworkReviewRuleService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,15 +1,15 @@
package com.competition.modules.biz.homework.controller;
package com.lesingle.modules.biz.homework.controller;
import com.competition.common.enums.ErrorCode;
import com.competition.common.exception.BusinessException;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.homework.dto.CreateHomeworkScoreDto;
import com.competition.modules.biz.homework.entity.BizHomeworkScore;
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
import com.competition.modules.biz.homework.service.IHomeworkScoreService;
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.enums.ErrorCode;
import com.lesingle.common.exception.BusinessException;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.homework.dto.CreateHomeworkScoreDto;
import com.lesingle.modules.biz.homework.entity.BizHomeworkScore;
import com.lesingle.modules.biz.homework.entity.BizHomeworkSubmission;
import com.lesingle.modules.biz.homework.service.IHomeworkScoreService;
import com.lesingle.modules.biz.homework.service.IHomeworkSubmissionService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,12 +1,12 @@
package com.competition.modules.biz.homework.controller;
package com.lesingle.modules.biz.homework.controller;
import com.competition.common.result.PageResult;
import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.homework.dto.CreateSubmissionDto;
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
import com.competition.security.annotation.RequirePermission;
import com.lesingle.common.result.PageResult;
import com.lesingle.common.result.Result;
import com.lesingle.common.util.SecurityUtil;
import com.lesingle.modules.biz.homework.dto.CreateSubmissionDto;
import com.lesingle.modules.biz.homework.entity.BizHomeworkSubmission;
import com.lesingle.modules.biz.homework.service.IHomeworkSubmissionService;
import com.lesingle.security.annotation.RequirePermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.homework.dto;
package com.lesingle.modules.biz.homework.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.homework.dto;
package com.lesingle.modules.biz.homework.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.homework.dto;
package com.lesingle.modules.biz.homework.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

View File

@ -1,4 +1,4 @@
package com.competition.modules.biz.homework.dto;
package com.lesingle.modules.biz.homework.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

Some files were not shown because too many files have changed in this diff Show More