Compare commits

...

55 Commits

Author SHA1 Message Date
sugarBecomer 92363c8185 ✨ 学习通 2025-11-26 12:34:43 +08:00
sugarBecomer 73b6c01cf9 ✨ 刷课表 2025-11-26 10:58:22 +08:00
sugarBecomer f703344485 ✨ 学习通刷课功能 2025-11-26 10:39:01 +08:00
sugarBecomer d9049095e3 ✨ 首页统计 2025-11-26 09:58:32 +08:00
sugarBecomer b2f0f91712 🐛 rsa非标准加密 2025-11-25 22:45:45 +08:00
sugarBecomer ac93a5385e 🐛 遗漏的代码 2025-11-25 19:01:43 +08:00
sugarBecomer 644ceed436 ✨ 学习通相关 2025-11-25 18:59:45 +08:00
sugarBecomer 77ea55eda2 🎨 调整扣分规则 2025-11-25 18:59:26 +08:00
sugarBecomer 0f83cfc9b9 ✨ 适配华侨大学 2025-11-23 20:55:54 +08:00
sugarBecomer 83749fc647 🐛 提交验证码处理 2025-11-22 22:49:32 +08:00
sugarBecomer bd5f8a9b15 🐛 经纬度修复 2025-11-22 20:01:21 +08:00
sugarBecomer 8889be8ca2 🐛 经纬度修复 2025-11-22 19:57:28 +08:00
sugarBecomer 6d58222143 ✨ 修复cookie问题 2025-11-22 15:50:31 +08:00
sugarBecomer c3019b741c 🐛 甘肃农业大学适配 2025-11-22 15:28:13 +08:00
sugarBecomer b5b2103372 🐛 学校登录模式更改 2025-11-22 14:11:10 +08:00
sugarBecomer b234c87048 ✨ 经纬度更新 2025-11-22 13:33:25 +08:00
sugarBecomer f982aa5e0c 🚀 删除冗余import 2025-11-21 20:32:23 +08:00
sugarBecomer 22823328c0 🚀 Dockerfile 2025-11-21 16:18:13 +08:00
sugarBecomer 323e49e375 🚀 captcha分离 2025-11-21 16:14:19 +08:00
sugarBecomer 526e4ac198 🐛 没有任务id了,因此没法删除asynq里的任务了 2025-11-18 16:05:03 +08:00
sugarBecomer 7469813e37 🐛 浅浅的加个锁 2025-11-18 15:53:57 +08:00
sugarBecomer 8eb4e40b57 🐛 缓存高频访问的学校信息 2025-11-18 15:37:08 +08:00
sugarBecomer 311cf18708 🐛 每次直接创建全新client试试效果 2025-11-18 14:57:37 +08:00
sugarBecomer 1bde0af01d 🐛 小问题修复 2025-11-18 14:34:43 +08:00
sugarBecomer 765ec6fb32 🐛 修复cookie问题 2025-11-17 17:14:49 +08:00
sugarBecomer 5cd8329519 🐛 修改定时任务信息 2025-11-17 12:39:05 +08:00
sugarBecomer 1ea9170d89 Merge remote-tracking branch 'origin/main' 2025-11-17 12:05:37 +08:00
sugarBecomer 29fc69d022 🐛 尝试解决cookie jar的问题 2025-11-17 12:03:47 +08:00
RecordingTheSmile 09e7a82882 fix: iap login failed message 2025-11-16 19:51:35 +08:00
sugarBecomer 1fff983d37 🐛 尝试干掉submitData的userId看看是否解决 2025-11-16 19:14:32 +08:00
RecordingTheSmile 836e61b8b0 fix: duplicate asynq.Unique option. 2025-11-14 22:36:01 +08:00
sugarBecomer 96044b8a88 Merge remote-tracking branch 'origin/main' 2025-11-14 22:35:32 +08:00
sugarBecomer acb865596e 🐛 取消定时任务cookie检测,强制登录 2025-11-14 22:35:15 +08:00
RecordingTheSmile 59559f4201 fix: task id unique 2025-11-14 22:26:10 +08:00
sugarBecomer 4d516adea8 ✨ 新增适配安徽建筑大学登录 2025-11-14 21:48:56 +08:00
sugarBecomer a37368ddcf ✨ 新增适配山东华宇工学院滑块 2025-11-14 20:38:58 +08:00
sugarBecomer 26dcfaf82d 🐛 增加日志信息 2025-11-14 13:09:00 +08:00
sugarBecomer e55a8866b1 🐛 schedule不要任务id唯一 2025-11-13 23:28:32 +08:00
sugarBecomer 0702135db2 🐛 iap新版本登录 2025-11-13 22:39:01 +08:00
sugarBecomer c668a76a99 🐛 关联关系bug 2025-11-13 21:42:26 +08:00
sugarBecomer 2a1cb90766 🐛 紧急修复漏洞 2025-11-13 21:20:07 +08:00
sugarBecomer ce536eb5bc 🐛 cookie问题 2025-11-13 16:53:59 +08:00
sugarBecomer 0244e330fd 🐛 cookie问题 2025-11-13 16:49:36 +08:00
sugarBecomer 035bac10cc 🐛 尝试调整登录cookie校验 2025-11-13 11:46:44 +08:00
sugarBecomer cb808f9c68 ✨ 适配南昌理工学院 2025-11-13 00:17:49 +08:00
sugarBecomer 22735aeaae ✨ 随机图片改为顺序图片 2025-11-12 19:59:02 +08:00
sugarBecomer dd6eeb5dfe 🐛 opencv 2025-11-12 15:54:52 +08:00
sugarBecomer ac6409f3e2 🐛 opencv 2025-11-12 15:39:20 +08:00
sugarBecomer 36a6932056 🐛 opencv 2025-11-12 15:19:15 +08:00
sugarBecomer 72dab0e968 🐛 opencv 2025-11-12 15:01:33 +08:00
sugarBecomer b716434f62 🐛 opencv 2025-11-12 14:45:25 +08:00
sugarBecomer 8ed7dd77e4 🐛 opencv 2025-11-12 14:30:03 +08:00
sugarBecomer ae1c9c695e 🐛 签到问题 & opencv 2025-11-12 14:12:47 +08:00
sugarBecomer 66995c0c78 🐛 cas滑块验证码 2025-11-12 13:35:17 +08:00
sugarBecomer 120e690795 🐛 cas滑块验证码 2025-11-12 13:31:54 +08:00
128 changed files with 12826 additions and 2450 deletions
+5 -8
View File
@@ -1,19 +1,16 @@
FROM golang:latest AS backend-builder
RUN apt update
RUN apt install -y build-essential
FROM golang:alpine AS backend-builder
WORKDIR /build
COPY . .
ENV CGO_ENABLED=1
RUN go build -ldflags="-s -w" -o bin/server
RUN apt install -y upx-ucl
RUN apk add --no-cache upx
RUN upx --best --lzma bin/server
FROM golang:latest
FROM alpine:latest
COPY --from=backend-builder /build/bin/server /app/server
#COPY static /app/static
#COPY static/*.so /app/
WORKDIR /app
EXPOSE 8080
RUN ls -a
+13 -4
View File
@@ -18,8 +18,9 @@ balanceMigrateUrl = "http://localhost:3200/balance-migrate/"
# 可选值: dev, prod
mode = "dev"
[ruoliCaptcha]
baseUrl = ""
[captcha]
baseUrl = "http://captcha-resolver.out"
#baseUrl = "http://localhost:8890"
[redis]
url = "redis://:jhkdjhkjdhsIUTYURTU_pEGmFP@database.out:56379"
@@ -42,6 +43,14 @@ baseUrl = "http://s3.out"
bucket = "likedream"
[cpdaily]
appVersion = "5.7.0"
appVersion = "9.7.2"
systemVersion = "iOS 17"
calVersion = "3.0.0"
calVersion = "firstv"
sliderIds = ["1018764144934965", "sdhyxy", "hqdx"]
[basePoint]
cpdaily = "0.5"
[task]
concurrency = 20
+403
View File
@@ -0,0 +1,403 @@
# 刷课接口文档
## 接口列表
### 1. 获取刷课列表
**接口地址**: `GET /course`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| type | string | 否 | 类型,可选值:`xxt`(学习通) |
| username | string | 否 | 账号(模糊搜索) |
| status | string | 否 | 状态,可选值:`not_start`(未开始)、`waiting`(等待中)、`doing`(进行中)、`done`(已完成)、`fail`(失败)、`pause`(暂停) |
| page | int | 否 | 页码,默认 1 |
| size | int | 否 | 每页数量,默认 10 |
**返回示例**:
```json
{
"code": 200,
"message": "success",
"data": [
{
"id": 1,
"user_id": 1,
"username": "学习通账号",
"password": "",
"type": "xxt",
"course_name": "大学英语",
"course_id": "123456",
"need": "task",
"status": "waiting",
"task_count": 10,
"success_count": 5,
"exam_state": "not_start",
"violence": "no",
"desc": "备注信息",
"create_time": "2024-01-01T00:00:00Z",
"update_time": "2024-01-01T00:00:00Z"
}
],
"total": 100
}
```
**字段说明**:
- `type`: 类型 - `xxt`(学习通)
- `need`: 需要刷的内容 - `task`(任务点)、`exam`(考试)、`all`(全部)
- `status`: 状态 - `not_start`(未开始)、`waiting`(等待中)、`doing`(进行中)、`done`(已完成)、`fail`(失败)、`pause`(暂停)
- `exam_state`: 考试状态 - `not_start`(未开始)、`waiting`(等待中)、`doing`(进行中)、`done`(已完成)、`fail`(失败)
- `violence`: 是否暴力刷课 - `no`(否)、`yes`(是)
---
### 2. 查询课程列表
**接口地址**: `POST /course/query`
**说明**: 通过学习通账号密码查询当前可选的课程列表,用于在添加刷课前选择课程。
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| username | string | 是 | 学习通账号 |
| password | string | 是 | 学习通密码 |
**请求示例**:
```json
{
"username": "学习通账号",
"password": "学习通密码"
}
```
**返回示例**:
```json
{
"code": 200,
"message": "success",
"data": [
{
"cpi": 123456,
"key": "234567",
"courseId": "345678",
"chatId": "456789",
"courseTeacher": "张老师",
"courseName": "大学英语",
"courseImage": "https://example.com/image.jpg",
"courseImageBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAA...",
"courseDataId": 567890,
"ContentID": 678901,
"isstart": true,
"state": 0
}
]
}
```
**字段说明**:
- `cpi`: 用户唯一标识
- `key`: 课程班级ID(classId)
- `courseId`: 课程ID(**添加刷课时需要使用**)
- `chatId`: 聊天ID
- `courseTeacher`: 授课教师
- `courseName`: 课程名称(**添加刷课时需要使用**)
- `courseImage`: 课程封面图片URL
- `courseImageBase64`: 课程封面图片Base64编码(包含Data URL前缀,可直接用于img标签的src属性)
- `courseDataId`: 课程数据ID
- `ContentID`: 内容ID
- `isstart`: 是否已开课(true:已开课,false:未开课)
- `state`: 课程状态(0:正常,1:已结束)
**注意事项**:
- `courseImageBase64` 字段已经包含了 Data URL 前缀(如 `data:image/jpeg;base64,`),可以直接设置到 `<img>` 标签的 `src` 属性
- 如果图片下载失败,`courseImageBase64` 字段将为空字符串,此时可以使用 `courseImage` URL 作为备用
**错误示例**:
```json
{
"code": 400,
"message": "登录失败,请检查账号密码",
"data": null
}
```
---
### 3. 添加刷课
**接口地址**: `POST /course`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| username | string | 是 | 学习通账号 |
| password | string | 是 | 学习通密码 |
| type | string | 是 | 类型,目前只支持 `xxt`(学习通) |
| course_name | string | 是 | 课程名称 |
| course_id | string | 是 | 课程ID |
| need | string | 是 | 需要刷的内容,可选值:`task`(任务点)、`exam`(考试)、`all`(全部) |
| violence | string | 否 | 是否暴力刷课,可选值:`no`(否,默认)、`yes`(是) |
| desc | string | 否 | 备注信息 |
**请求示例**:
```json
{
"username": "学习通账号",
"password": "学习通密码",
"type": "xxt",
"course_name": "大学英语",
"course_id": "123456",
"need": "task",
"violence": "no",
"desc": "备注信息"
}
```
**返回示例**:
```json
{
"code": 200,
"message": "添加成功",
"data": null
}
```
**错误示例**:
```json
{
"code": 400,
"message": "该课程已存在刷课任务",
"data": null
}
```
---
### 4. 修改刷课密码
**接口地址**: `PUT /course/password/{id}`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | int64 | 是 | 刷课任务ID |
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| password | string | 是 | 新密码 |
**请求示例**:
```json
{
"password": "新密码"
}
```
**返回示例**:
```json
{
"code": 200,
"message": "密码修改成功",
"data": null
}
```
**错误示例**:
```json
{
"code": 400,
"message": "课程不存在或无权限操作",
"data": null
}
```
---
### 5. 重刷课程
**接口地址**: `POST /course/restart/{id}`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | int64 | 是 | 刷课任务ID |
**说明**: 只有状态为 `done`(已完成)的课程才能重刷,重刷后会重置课程状态、任务数量、成功数量和考试状态。
**返回示例**:
```json
{
"code": 200,
"message": "重刷成功",
"data": null
}
```
**错误示例**:
```json
{
"code": 400,
"message": "只有已完成的课程才能重刷",
"data": null
}
```
---
### 6. 暂停/恢复刷课
**接口地址**: `POST /course/pause/{id}`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | int64 | 是 | 刷课任务ID |
**说明**: 该接口会在暂停和恢复之间切换:
- 如果当前状态是 `pause`(暂停),则恢复为 `waiting`(等待中)
- 如果当前状态不是暂停,则设置为 `pause`(暂停)
**返回示例(暂停)**:
```json
{
"code": 200,
"message": "已暂停刷课",
"data": null
}
```
**返回示例(恢复)**:
```json
{
"code": 200,
"message": "已恢复刷课",
"data": null
}
```
---
### 7. 删除刷课
**接口地址**: `DELETE /course/{id}`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | int64 | 是 | 刷课任务ID |
**返回示例**:
```json
{
"code": 200,
"message": "删除成功",
"data": null
}
```
**错误示例**:
```json
{
"code": 400,
"message": "课程不存在或无权限操作",
"data": null
}
```
---
## 枚举值说明
### 类型 (type)
| 值 | 说明 |
|-----|------|
| xxt | 学习通 |
### 需要刷的内容 (need)
| 值 | 说明 |
|-----|------|
| task | 任务点 |
| exam | 考试 |
| all | 全部 |
### 状态 (status)
| 值 | 说明 |
|-----|------|
| not_start | 未开始 |
| waiting | 等待中 |
| doing | 进行中 |
| done | 已完成 |
| fail | 失败 |
| pause | 暂停 |
### 考试状态 (exam_state)
| 值 | 说明 |
|-----|------|
| not_start | 未开始 |
| waiting | 等待中 |
| doing | 进行中 |
| done | 已完成 |
| fail | 失败 |
### 是否暴力刷课 (violence)
| 值 | 说明 |
|-----|------|
| no | 否 |
| yes | 是 |
---
## 通用说明
### 认证
所有接口都需要用户认证,需要在请求头中携带认证信息(具体认证方式根据项目配置)。
### 权限
用户只能操作自己创建的刷课任务,无法访问或修改其他用户的数据。
### 错误码
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 业务错误(如参数错误、权限不足等) |
| 401 | 未认证 |
| 500 | 服务器错误 |
-3
View File
@@ -19,18 +19,15 @@ require (
github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.1
github.com/joomcode/errorx v1.2.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/redis/go-redis/v9 v9.11.0
github.com/samber/oops v1.19.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/shopspring/decimal v1.4.0
github.com/thedevsaddam/gojsonq v1.9.1
github.com/tidwall/gjson v1.18.0
github.com/tidwall/match v1.1.1
github.com/wenlng/go-captcha-assets v1.0.7
github.com/wenlng/go-captcha/v2 v2.0.4
github.com/wneessen/go-mail v0.6.2
github.com/yalue/onnxruntime_go v1.12.1
go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.43.0
-6
View File
@@ -150,8 +150,6 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@@ -199,8 +197,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thedevsaddam/gojsonq v1.9.1 h1:zQulEP43nwmq5EKrNWyIgJVbqDeMdC1qzXM/f5O15a0=
github.com/thedevsaddam/gojsonq v1.9.1/go.mod h1:RBcQaITThgJAAYKH7FNp2onYodRz8URfsuEGpAch0NA=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -219,8 +215,6 @@ github.com/wenlng/go-captcha/v2 v2.0.4 h1:5cSUF36ZyA03qeDMjKmeXGpbYJMXEexZIYK3Vg
github.com/wenlng/go-captcha/v2 v2.0.4/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34=
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/yalue/onnxruntime_go v1.12.1 h1:joCCmBnNjHy04jK9EMP/UV6oPPqySXlRgf3gcUcnI/g=
github.com/yalue/onnxruntime_go v1.12.1/go.mod h1:b4X26A8pekNb1ACJ58wAXgNKeUCGEAQ9dmACut9Sm/4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+5
View File
@@ -6,6 +6,7 @@ import (
"likedream-goframe/internal/controller/agent"
"likedream-goframe/internal/controller/card"
"likedream-goframe/internal/controller/common"
"likedream-goframe/internal/controller/course"
"likedream-goframe/internal/controller/cpdaily"
"likedream-goframe/internal/controller/menu"
"likedream-goframe/internal/controller/music"
@@ -54,8 +55,12 @@ func StartServer(ctx context.Context) {
cpdailyGroup.Bind(cpdaily.NewCpTaskCtrlV1())
cpdailyGroup.Bind(cpdaily.NewCpImageCtrlV1())
cpdailyGroup.Bind(cpdaily.NewCpTaskLogCtrlV1())
// 娱乐
musicGroup := group.Group("/music").Middleware(middleware.AuthHandler, middleware.PermissionHandler)
musicGroup.Bind(music.NewMusicCtrlV1())
// 网课
courseGroup := group.Group("/course").Middleware(middleware.AuthHandler, middleware.PermissionHandler)
courseGroup.Bind(course.NewXxtCtrlV1())
})
s.SetPort(port.Int())
+55
View File
@@ -0,0 +1,55 @@
package v1
import (
"likedream-goframe/internal/ent/courseentity"
"github.com/gogf/gf/v2/frame/g"
)
type CourseListReq struct {
g.Meta `path:"/course" method:"get" tags:"Course" summary:"获取刷课列表"`
Type courseentity.Type `json:"type"`
Username string `json:"username"`
Status courseentity.Status `json:"status"`
Page int `json:"page" d:"1"`
Size int `json:"size" d:"10"`
}
type AddCourseReq struct {
g.Meta `path:"/course" method:"post" tags:"Course" summary:"添加刷课"`
Username string `json:"username" v:"required#账号不能为空"`
Password string `json:"password" v:"required#密码不能为空"`
Type courseentity.Type `json:"type" v:"required#类型不能为空"`
CourseName string `json:"course_name" v:"required#课程名称不能为空"`
CourseID string `json:"course_id" v:"required#课程ID不能为空"`
Need courseentity.Need `json:"need" v:"required#需要刷的内容不能为空"`
Violence courseentity.Violence `json:"violence" d:"no"`
Desc string `json:"desc"`
}
type UpdateCoursePasswordReq struct {
g.Meta `path:"/course/password/{id}" method:"put" tags:"Course" summary:"修改刷课密码"`
ID int64 `json:"id"`
Password string `json:"password" v:"required#密码不能为空"`
}
type RestartCourseReq struct {
g.Meta `path:"/course/restart/{id}" method:"post" tags:"Course" summary:"重刷课程"`
ID int64 `json:"id"`
}
type PauseCourseReq struct {
g.Meta `path:"/course/pause/{id}" method:"post" tags:"Course" summary:"暂停刷课"`
ID int64 `json:"id"`
}
type DeleteCourseReq struct {
g.Meta `path:"/course/{id}" method:"delete" tags:"Course" summary:"删除刷课"`
ID int64 `json:"id"`
}
type QueryCourseReq struct {
g.Meta `path:"/course/query" method:"post" tags:"Course" summary:"查询课程列表"`
Username string `json:"username" v:"required#账号不能为空"`
Password string `json:"password" v:"required#密码不能为空"`
}
+8
View File
@@ -0,0 +1,8 @@
package v1
// TaskStatisticsItem 每日任务统计项
type TaskStatisticsItem struct {
Date string `json:"date"` // 日期 格式:2006-01-02
PendingTask int `json:"pendingTask"` // 需要完成的任务数
SuccessTask int `json:"successTask"` // 成功执行的任务数
}
+9
View File
@@ -67,3 +67,12 @@ type PauseTaskReq struct {
g.Meta `path:"/task/pause/{id}" method:"post" tags:"CpTask" summary:"暂停任务"`
Id int64 `json:"id"`
}
type FindLocationReq struct {
g.Meta `path:"/task/location" method:"get" tags:"CpTask" summary:"查找地址"`
Address string `json:"address"`
}
type GetTaskStatisticsReq struct {
g.Meta `path:"/task/statistics" method:"get" tags:"CpTask" summary:"获取近七日任务统计数据"`
}
+20 -1
View File
@@ -1,6 +1,10 @@
package v1
import "github.com/gogf/gf/v2/frame/g"
import (
"likedream-goframe/internal/framework/captcha"
"github.com/gogf/gf/v2/frame/g"
)
type CpSchoolListReq struct {
g.Meta `path:"/school" method:"get" tags:"CpSchool" summary:"获取cp学校列表"`
@@ -14,6 +18,21 @@ type CpUserAddReq struct {
SchoolId string `json:"school_id"`
Email string `json:"email" v:"email|regex:^.*@qq\\.com$#邮箱格式不正确|必须是QQ邮箱"`
}
type LoginGetCode struct {
g.Meta `path:"/user/getCode" method:"post" tags:"CpUser" summary:"获取手机号登录验证码"`
Mobile string `json:"mobile" v:"required#手机号不能为空"`
CaptchaKey string `json:"captchaKey" v:"required#验证码不能不能为空"`
Dots []captcha.ConfirmData `json:"dots" v:"required#验证码不能为空"`
}
type AddByCode struct {
g.Meta `path:"/user/addByCode" method:"post" tags:"CpUser" summary:"手机号登录"`
CaptchaKey string `json:"captchaKey" v:"required#验证码不能不能为空"`
Dots []captcha.ConfirmData `json:"dots" v:"required#验证码不能为空"`
Mobile string `json:"mobile" v:"required#手机号不能为空"`
Code string `json:"code"`
}
type CpUserConfirmAddReq struct {
g.Meta `path:"/user/confirm" method:"post" tags:"CpUser" summary:"多因子确认添加cp用户"`
Key string `json:"key"`
@@ -0,0 +1,56 @@
package captcha
import (
"encoding/base64"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/cst"
"likedream-goframe/internal/utils"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/samber/oops"
"github.com/tidwall/gjson"
)
func ApiSemiOCRVerification(bytes []byte, shape1, shape2 int64) string {
data := Bytes2Base64(bytes)
ctx := gctx.GetInitCtx()
baseUrl := g.Config().MustGetWithEnv(ctx, cst.ConfigCaptchaBaseUrl).String() + "/semiOcr"
client := httpclient.Cpdaily.Clone(ctx)
client.SetDoNotParseResponse(false)
post, err := client.R().SetBody(map[string]any{
"base64": data,
"shape1": shape1,
"shape2": shape2,
}).Post(baseUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("SemiOCRVerification", baseUrl).
WithContext(ctx).
Wrap(err)
})
result := post.String()
return gjson.Get(result, "data").String()
}
func ApiAutoOCRVerification(bytes []byte) string {
data := Bytes2Base64(bytes)
ctx := gctx.GetInitCtx()
baseUrl := g.Config().MustGetWithEnv(ctx, cst.ConfigCaptchaBaseUrl).String() + "/ocr"
client := httpclient.Cpdaily.Clone(ctx)
client.SetDoNotParseResponse(false)
post, err := client.R().SetBody(map[string]any{
"base64": data,
}).Post(baseUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("AutoOCRVerification", baseUrl).
WithContext(ctx).
Wrap(err)
})
result := post.String()
return gjson.Get(result, "data").String()
}
func Bytes2Base64(data []byte) string {
return base64.StdEncoding.EncodeToString(data)
}
+119 -46
View File
@@ -8,20 +8,21 @@ import (
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"strings"
"sync"
)
type domainMap map[string]cookieMap
// AllSavePersistCookieJar 支持泛域名的 Cookie Jar
// 不同的二级域名会共享相同主域名的 cookies
// 例如: passport2.chaoxing.com 和 mooc1-api.chaoxing.com 共享 .chaoxing.com 的 cookies
// AllSavePersistCookieJar 完全无限制的 Cookie Jar
// 所有域名的 cookies 都保存在一起,访问任何域名都返回所有 cookies
// 不检查域名、路径等限制,适用于需要跨域共享 session 的场景
type AllSavePersistCookieJar struct {
domainMap domainMap
*sync.RWMutex
}
const globalDomain = "*" // 使用全局域名存储所有 cookies
func NewAllSavePersistCookieJar() *AllSavePersistCookieJar {
return &AllSavePersistCookieJar{
domainMap: make(domainMap),
@@ -29,45 +30,36 @@ func NewAllSavePersistCookieJar() *AllSavePersistCookieJar {
}
}
// extractMainDomain 提取主域名
// 例如: passport2.chaoxing.com -> chaoxing.com
//
// mooc1-api.chaoxing.com -> chaoxing.com
// www.example.co.uk -> example.co.uk
func extractMainDomain(host string) string {
// 移除端口号
if colonIndex := strings.Index(host, ":"); colonIndex != -1 {
host = host[:colonIndex]
}
parts := strings.Split(host, ".")
if len(parts) <= 2 {
// 已经是主域名或单级域名
return host
}
// 处理特殊的二级顶级域名,如 .co.uk, .com.cn 等
// 简化处理:取最后两部分
// 更复杂的情况可以使用 publicsuffix 包
return strings.Join(parts[len(parts)-2:], ".")
}
func (a *AllSavePersistCookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) {
a.Lock()
defer a.Unlock()
// 提取主域名
mainDomain := extractMainDomain(u.Host)
if _, exists := a.domainMap[mainDomain]; !exists {
a.domainMap[mainDomain] = make(cookieMap)
// 使用全局域名,所有 cookies 保存在一起
if _, exists := a.domainMap[globalDomain]; !exists {
a.domainMap[globalDomain] = make(cookieMap)
}
for _, cookie := range cookies {
if a.domainMap[mainDomain] == nil {
a.domainMap[mainDomain] = make(cookieMap)
if a.domainMap[globalDomain] == nil {
a.domainMap[globalDomain] = make(cookieMap)
}
// 查找是否存在相同 name 的 cookie(忽略 path 和 domain)
existingCookies := a.domainMap[globalDomain][cookie.Name]
found := false
for i, existing := range existingCookies {
if existing.Path == cookie.Path && existing.Domain == cookie.Domain {
// 找到完全匹配的 cookie,替换它
existingCookies[i] = cookie
found = true
break
}
}
if !found {
// 没有找到匹配的,添加新的 cookie
a.domainMap[globalDomain][cookie.Name] = append(a.domainMap[globalDomain][cookie.Name], cookie)
}
a.domainMap[mainDomain][cookie.Name] = cookie
}
}
@@ -75,13 +67,21 @@ func (a *AllSavePersistCookieJar) Cookies(u *url.URL) []*http.Cookie {
a.RLock()
defer a.RUnlock()
// 提取主域名
mainDomain := extractMainDomain(u.Host)
// 直接返回全局域名下的所有 cookies,不检查域名和路径
if cookiesList, exists := a.domainMap[globalDomain]; exists {
result := make([]*http.Cookie, 0, len(cookiesList))
if cookies, exists := a.domainMap[mainDomain]; exists {
result := make([]*http.Cookie, 0, len(cookies))
for _, cookie := range cookies {
result = append(result, cookie)
for _, cookies := range cookiesList {
// 如果只有一个 cookie,直接返回
if len(cookies) == 1 {
result = append(result, cookies[0])
continue
}
// 有多个同名 cookie,返回最后设置的那个(列表最后一个)
if len(cookies) > 0 {
result = append(result, cookies[len(cookies)-1])
}
}
return result
}
@@ -109,7 +109,30 @@ func (a *AllSavePersistCookieJar) Deserialize(content string) {
var domainMap domainMap
err := sonic.Unmarshal([]byte(content), &domainMap)
utils.OkOrPanic(err)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldDomainMap map[string]oldCookieMap
var oldMap oldDomainMap
oldErr := sonic.Unmarshal([]byte(content), &oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
domainMap = make(map[string]cookieMap)
for domain, cookies := range oldMap {
domainMap[domain] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
domainMap[domain][name] = []*http.Cookie{cookie}
}
}
}
a.domainMap = domainMap
}
@@ -142,7 +165,31 @@ func (a *AllSavePersistCookieJar) DeserializeBytes(content []byte) {
var domainMap domainMap
err := gob.NewDecoder(retReader).
Decode(&domainMap)
utils.OkOrPanic(err)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldDomainMap map[string]oldCookieMap
retReader = bytes.NewBuffer(content)
var oldMap oldDomainMap
oldErr := gob.NewDecoder(retReader).Decode(&oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
domainMap = make(map[string]cookieMap)
for domain, cookies := range oldMap {
domainMap[domain] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
domainMap[domain][name] = []*http.Cookie{cookie}
}
}
}
a.domainMap = domainMap
}
@@ -151,10 +198,36 @@ func (a *AllSavePersistCookieJar) DeserializeReader(reader io.Reader) {
a.Lock()
defer a.Unlock()
var domainMap domainMap
err := gob.NewDecoder(reader).
Decode(&domainMap)
// 先读取所有数据,以便在失败时可以重试
data, err := io.ReadAll(reader)
utils.OkOrPanic(err)
var domainMap domainMap
err = gob.NewDecoder(bytes.NewBuffer(data)).Decode(&domainMap)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldDomainMap map[string]oldCookieMap
var oldMap oldDomainMap
oldErr := gob.NewDecoder(bytes.NewBuffer(data)).Decode(&oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
domainMap = make(map[string]cookieMap)
for domain, cookies := range oldMap {
domainMap[domain] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
domainMap[domain][name] = []*http.Cookie{cookie}
}
}
}
a.domainMap = domainMap
}
+234 -16
View File
@@ -3,17 +3,19 @@ package cookiejar
import (
"bytes"
"encoding/gob"
"github.com/bytedance/sonic"
"io"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"strings"
"sync"
"github.com/bytedance/sonic"
)
type hostMap map[string]cookieMap
type cookieMap map[string]*http.Cookie
type cookieMap map[string][]*http.Cookie
type PersistCookieJar struct {
hostMap hostMap
*sync.RWMutex
@@ -33,14 +35,35 @@ func (p *PersistCookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) {
host := u.Host
if _, exists := p.hostMap[host]; !exists {
p.hostMap = make(hostMap)
p.hostMap[host] = make(cookieMap)
}
for _, cookie := range cookies {
if p.hostMap[host] == nil {
p.hostMap[host] = make(cookieMap)
}
p.hostMap[host][cookie.Name] = cookie
// 检查并删除全局 cookie 中同名的 cookie
if globalCookies, exists := p.hostMap[""]; exists {
delete(globalCookies, cookie.Name)
}
// 查找是否存在相同 name, path 和 domain 的 cookie
existingCookies := p.hostMap[host][cookie.Name]
found := false
for i, existing := range existingCookies {
if existing.Path == cookie.Path && existing.Domain == cookie.Domain {
// 找到完全匹配的 cookie,替换它
existingCookies[i] = cookie
found = true
break
}
}
if !found {
// 没有找到匹配的,添加新的 cookie
p.hostMap[host][cookie.Name] = append(p.hostMap[host][cookie.Name], cookie)
}
}
}
@@ -50,14 +73,110 @@ func (p *PersistCookieJar) Cookies(u *url.URL) []*http.Cookie {
defer p.RUnlock()
host := u.Host
if cookies, exists := p.hostMap[host]; exists {
result := make([]*http.Cookie, 0, len(cookies))
for _, cookie := range cookies {
result = append(result, cookie)
}
return result
requestPath := u.Path
if requestPath == "" {
requestPath = "/"
}
return nil
result := make([]*http.Cookie, 0)
addedNames := make(map[string]bool)
// 先添加匹配 host 的 cookie
if cookiesList, exists := p.hostMap[host]; exists {
for cookieName, cookies := range cookiesList {
// 如果只有一个 cookie,直接返回
if len(cookies) == 1 {
result = append(result, cookies[0])
addedNames[cookieName] = true
continue
}
// 有多个同名 cookie,选择最匹配的(path 最长且匹配请求路径的)
var bestMatch *http.Cookie
bestMatchLen := -1
for _, cookie := range cookies {
cookiePath := cookie.Path
if cookiePath == "" {
cookiePath = "/"
}
// 检查 path 是否匹配
if pathMatch(requestPath, cookiePath) {
if len(cookiePath) > bestMatchLen {
bestMatch = cookie
bestMatchLen = len(cookiePath)
}
}
}
// 如果找到匹配的,添加到结果中
if bestMatch != nil {
result = append(result, bestMatch)
addedNames[cookieName] = true
}
}
}
// 再添加没有 host 的全局 cookie(避免重复)
if globalCookies, exists := p.hostMap[""]; exists {
for cookieName, cookies := range globalCookies {
// 如果该名称的 cookie 已经从 host 中添加了,跳过
if addedNames[cookieName] {
continue
}
// 如果只有一个 cookie,直接返回
if len(cookies) == 1 {
result = append(result, cookies[0])
continue
}
// 有多个同名 cookie,选择最匹配的
var bestMatch *http.Cookie
bestMatchLen := -1
for _, cookie := range cookies {
cookiePath := cookie.Path
if cookiePath == "" {
cookiePath = "/"
}
if pathMatch(requestPath, cookiePath) {
if len(cookiePath) > bestMatchLen {
bestMatch = cookie
bestMatchLen = len(cookiePath)
}
}
}
if bestMatch != nil {
result = append(result, bestMatch)
}
}
}
return result
}
// pathMatch 检查请求路径是否匹配 cookie 的 path
// 根据 RFC 6265 的规则
func pathMatch(requestPath, cookiePath string) bool {
if requestPath == cookiePath {
return true
}
if strings.HasPrefix(requestPath, cookiePath) {
// cookiePath 以 / 结尾,或者 requestPath 的下一个字符是 /
if strings.HasSuffix(cookiePath, "/") {
return true
}
if len(requestPath) > len(cookiePath) && requestPath[len(cookiePath)] == '/' {
return true
}
}
return false
}
func (p *PersistCookieJar) Serialize() string {
@@ -83,7 +202,29 @@ func (p *PersistCookieJar) Deserialize(content string) {
var hostMap hostMap
err := sonic.Unmarshal([]byte(content), &hostMap)
utils.OkOrPanic(err)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldHostMap map[string]oldCookieMap
var oldMap oldHostMap
oldErr := sonic.Unmarshal([]byte(content), &oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
hostMap = make(map[string]cookieMap)
for host, cookies := range oldMap {
hostMap[host] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
hostMap[host][name] = []*http.Cookie{cookie}
}
}
}
p.hostMap = hostMap
}
@@ -116,7 +257,31 @@ func (p *PersistCookieJar) DeserializeBytes(content []byte) {
var hostMap hostMap
err := gob.NewDecoder(retReader).
Decode(&hostMap)
utils.OkOrPanic(err)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldHostMap map[string]oldCookieMap
retReader = bytes.NewBuffer(content)
var oldMap oldHostMap
oldErr := gob.NewDecoder(retReader).Decode(&oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
hostMap = make(map[string]cookieMap)
for host, cookies := range oldMap {
hostMap[host] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
hostMap[host][name] = []*http.Cookie{cookie}
}
}
}
p.hostMap = hostMap
}
@@ -125,10 +290,63 @@ func (p *PersistCookieJar) DeserializeReader(reader io.Reader) {
p.Lock()
defer p.Unlock()
var hostMap hostMap
err := gob.NewDecoder(reader).
Decode(&hostMap)
// 先读取所有数据,以便在失败时可以重试
data, err := io.ReadAll(reader)
utils.OkOrPanic(err)
var hostMap hostMap
err = gob.NewDecoder(bytes.NewBuffer(data)).Decode(&hostMap)
// 如果反序列化失败,尝试从旧格式转换
if err != nil {
// 旧格式: map[string]map[string]*http.Cookie
type oldCookieMap map[string]*http.Cookie
type oldHostMap map[string]oldCookieMap
var oldMap oldHostMap
oldErr := gob.NewDecoder(bytes.NewBuffer(data)).Decode(&oldMap)
if oldErr != nil {
// 两种格式都失败,抛出原始错误
utils.OkOrPanic(err)
return
}
// 转换旧格式到新格式
hostMap = make(map[string]cookieMap)
for host, cookies := range oldMap {
hostMap[host] = make(map[string][]*http.Cookie)
for name, cookie := range cookies {
hostMap[host][name] = []*http.Cookie{cookie}
}
}
}
p.hostMap = hostMap
}
// RemoveCookieInfo 将所有 host 的 cookie 移除 host 信息,存储为全局 cookie
func (p *PersistCookieJar) RemoveCookieInfo() {
p.Lock()
defer p.Unlock()
// 确保全局 cookie map 存在
if _, exists := p.hostMap[""]; !exists {
p.hostMap[""] = make(cookieMap)
}
// 将所有 host 的 cookie 移动到全局
for host, hostCookies := range p.hostMap {
// 跳过全局 cookie 本身
if host == "" {
continue
}
// 将该 host 的所有 cookie 移动到全局
for cookieName, cookies := range hostCookies {
p.hostMap[""][cookieName] = cookies
}
// 删除该 host 的 cookie
delete(p.hostMap, host)
}
}
@@ -114,3 +114,10 @@ func TestPersistCookieJar_DeserializeReader(t *testing.T) {
}
}
}
func TestDeseralizeCookie(t *testing.T) {
var str = `{"cdpc.campusphere.net":{"MOD_AUTH_CAS":{"Name":"MOD_AUTH_CAS","Value":"ST-iap:1021988086918169:ST:8d7fe60b-dc7d-4fcd-b26b-5517e3ca1926:20251113162807","Quoted":false,"Path":"/","Domain":"","Expires":"0001-01-01T00:00:00Z","RawExpires":"","MaxAge":0,"Secure":false,"HttpOnly":true,"SameSite":0,"Partitioned":false,"Raw":"MOD_AUTH_CAS=ST-iap:1021988086918169:ST:8d7fe60b-dc7d-4fcd-b26b-5517e3ca1926:20251113162807; path=/; Httponly","Unparsed":null},"HWWAFSESID":{"Name":"HWWAFSESID","Value":"88d84731cf506df0bf","Quoted":false,"Path":"/","Domain":"","Expires":"0001-01-01T00:00:00Z","RawExpires":"","MaxAge":0,"Secure":false,"HttpOnly":false,"SameSite":0,"Partitioned":false,"Raw":"HWWAFSESID=88d84731cf506df0bf; path=/","Unparsed":null},"HWWAFSESTIME":{"Name":"HWWAFSESTIME","Value":"1763022486728","Quoted":false,"Path":"/","Domain":"","Expires":"0001-01-01T00:00:00Z","RawExpires":"","MaxAge":0,"Secure":false,"HttpOnly":false,"SameSite":0,"Partitioned":false,"Raw":"HWWAFSESTIME=1763022486728; path=/","Unparsed":null},"CONVERSATION":{"Name":"CONVERSATION","Value":"iap-1021988086918169-CONV-83bee566-7010-4ef6-ad17-4b9eebcccdf8","Quoted":false,"Path":"/iap","Domain":"","Expires":"0001-01-01T00:00:00Z","RawExpires":"","MaxAge":0,"Secure":false,"HttpOnly":true,"SameSite":0,"Partitioned":false,"Raw":"CONVERSATION=iap-1021988086918169-CONV-83bee566-7010-4ef6-ad17-4b9eebcccdf8; Path=/iap; HttpOnly","Unparsed":null},"CASTGC":{"Name":"CASTGC","Value":"iap-1021988086918169-TGT-717aa5db-92cb-46ac-a178-64b630946f43","Quoted":false,"Path":"/iap","Domain":"","Expires":"0001-01-01T00:00:00Z","RawExpires":"","MaxAge":0,"Secure":true,"HttpOnly":true,"SameSite":4,"Partitioned":false,"Raw":"CASTGC=iap-1021988086918169-TGT-717aa5db-92cb-46ac-a178-64b630946f43; Path=/iap; HttpOnly; Secure; SameSite=None","Unparsed":null}}}`
jar := NewPersistCookieJar()
jar.Deserialize(str)
t.Log(jar)
}
@@ -0,0 +1,15 @@
package cryptoutil
import (
"encoding/base64"
"testing"
)
func TestAesDecrypt(t *testing.T) {
str := "szFn6zAbjjU="
decodeString, _ := base64.StdEncoding.DecodeString(str)
decrypt := AesDecrypt(decodeString)
t.Log(string(decrypt))
encrypt := AesEncrypt([]byte("c9fe4f05-e1ed-4c15-aeb4-b05fa9bfb16f"))
t.Log(base64.StdEncoding.EncodeToString(encrypt))
}
@@ -0,0 +1,76 @@
package cryptoutil
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"net/url"
"strings"
"testing"
"github.com/gogf/gf/v2/container/gvar"
)
func TestDesDecrypt(t *testing.T) {
decodeString, _ := base64.StdEncoding.DecodeString("64JITpWPkKuGMhi1m7Ikxu1ekL6VQOdjmJvaQPI07MWbLETtJsxzkk2bCUVNaWgUULa3wS9LH+ANO5CUBeOXi02CcS/8lN/tYDFYCyuK7dLN+q0fD0gQlUsuHwy/78EkwZa9VrLh4e519vM9AxR2GnK4WGpumeH7TGJjDkLH0AitKfUYQnil9AKZEVgSSkHkg9ko64qj0QJvzmo76zOkMpIb1+1Pn2cO+5soTSl8BCsP/8hOP5HdOMVBCYH++iSK3O5sYz/cwZq/G8aJrqmEFcDHbgTouXyD5kxa4lb72Vw3Ku+eH+3qDwYMYxWrIcAm")
decrypt := DesDecrypt(decodeString, Cat)
str := "{\"lon\":\"113.045246\",\"lat\":\"28.077659\",\"model\":\"847b5b9c-2d97-41ec-8a5d-e9ac79da090d\",\"appVersion\":\"9.7.2\",\"userId\":\"430181200504150038\",\"systemName\":\"android\",\"deviceId\":\"09E46EC9-57C1-4724-BE53-5879CA80A2A0\",\"cpdailyVersion\":\"9.7.2\",\"systemVersion\":\"iOS 17\"}"
encrypt := DesEncrypt([]byte(str), Cat)
toString := base64.StdEncoding.EncodeToString(encrypt)
t.Log(toString)
t.Log(string(decrypt))
d2, _ := base64.StdEncoding.DecodeString("86JzX4MBsorI5KasKBHUj6orO1qcPWbo758Ux6G2NUoRuGCoZnE+cJW5slZvsflBrKITPeitF7GTisMNRJxRqlA8cLXAqyo9ZlBPPx0MM3KjILHiA/EvrRrmamo7vi1d/F0ChQc8jLfMgkkcOg4FBmM1H8Yg6KyYdMNN7anIbIdJ+6RPEsakjAIw0r4eawofSh5E0QclDry0v36GK3UA3GDBBfqSub8V5PRL7fQty/R0lx7+mfc2AIXXjUVsv85WQKoyyUwg3siWpQL5S1cbtQNUXClSQP1IAGROnBftvdrib01PR1h7Cp+kvE3HK0+/8l0FoGJ1aijff1t9T9MaVXM/jSUFwkA7tmZIjodrUDhZcCMNm5YLm1ZO4UVBQO118FyOBjSLet0nPc0Wg0/dTO0XZoo9e2bCQwjIQC2G8TdzHDxvDjGEc6hX++nZrN23n19bAgqdvzZt58KftznE9s5t4CEjiHx3ykyidGEGr/VEi/05uOtm8/EsFUVp8+MyVgaTxoaq/4LBu28L59U6+6mZjOFUt49STR5p3UPI3ZJQLe2lmnx9BdqrIHbdXgYNu5oNzrDM8m3z3ns9M2HEGg==")
d := AesDecryptForCat(d2)
t.Log(d)
form := map[string]*gvar.Var{
"appVersion": gvar.New("9.7.2"),
"bodyString": gvar.New("0u7hBd+6O11ZaGsT51FUAVfB72waqtTx5oQQLVEEAuCM29x8lQc41i9/dF6+9wjQNC05jdJTBmHlwFoLD09hfsnQWSyjl/ys124RUQL3r/Um5AwNdKiJE24YQsyQlP3ZamUIFlrNSxmn/DfZ/jR9oZhiVKZSlygA2LNLxtNOyk4rETfqKpZKFOnThbXy5Hf779jUDmPNwn1ZrdWN8UHm6wO+EahrrX60vC02VUHs7r3rMrDWZjBJ57eSgD0QwZabcIb/u9kx1AD8OAxINar3X+N+uAhfrxjHd6v6OVvnc2I9iPLEGNFRKZ94jq64hnATOSH6S/IR/QbDoxebWpGUXm3ALYZeoni09uyhG7IE5NtRw/OmqYtdknrs+c0qyOOBkvxPlqKo+SV4qq7xtrn9RWeZIigiyM8mVtdovN1T/x3DnHxd/AYoSS3/cGkg+2QHzWey/Xf5c4g0EvhaGlE0C7ejKIn5wp0XeHyvAjm4ZVyUhgTN2dZVCQ6ChxXOpLnVDWPNeeZ9vysqVl1ZSkQdCz20gy3p9RfYuArluQqjwIoWQe632Cc/Ep++99V5Tc9iIlLr93PfxFaJJfov3avLKw=="),
"calVersion": gvar.New("firstv"),
"deviceId": gvar.New("09E46EC9-57C1-4724-BE53-5879CA80A2A0"),
"lat": gvar.New("28.077659"),
"lon": gvar.New("113.045246"),
"model": gvar.New("847b5b9c-2d97-41ec-8a5d-e9ac79da090d"),
"systemName": gvar.New("android"),
"systemVersion": gvar.New("iOS 17"),
"userId": gvar.New("430181200504150038"),
"version": gvar.New("first_v4"),
}
sign := GetSubmitSign(form)
t.Log(sign)
}
const submitSignSalt = "hru89rwr87Nthifa" // 请根据实际情况替换为真实盐值
func GetSubmitSign(form map[string]*gvar.Var) string {
// 按照指定顺序构建哈希数据
keys := []string{
"appVersion",
"bodyString",
"deviceId",
"lat",
"lon",
"model",
"systemName",
"systemVersion",
"userId",
}
var sb strings.Builder
for _, key := range keys {
val := ""
if v, ok := form[key]; ok && v != nil {
val = v.String()
}
// 这里是 key=UrlEncode(key)&,注意是对 key 进行 url encode
sb.WriteString(key)
sb.WriteString("=")
sb.WriteString(url.QueryEscape(val))
sb.WriteString("&")
}
sb.WriteString(submitSignSalt)
// 计算MD5
hash := md5.Sum([]byte(sb.String()))
return hex.EncodeToString(hash[:])
}
@@ -0,0 +1,12 @@
package cryptoutil
import (
"crypto/md5"
"encoding/hex"
)
// MD5 对输入字符串进行 MD5 加密,返回 32 位小写十六进制字符串
func MD5(input string) string {
hash := md5.Sum([]byte(input))
return hex.EncodeToString(hash[:])
}
@@ -0,0 +1,838 @@
package cryptoutil
import (
"fmt"
"math"
"strings"
)
// RSA Constants
const (
biRadixBits = 16
bitsPerDigit = biRadixBits
biRadix = 1 << 16 // 65536
biHalfRadix = biRadix >> 1
biRadixSquared = biRadix * biRadix
maxDigitVal = biRadix - 1
maxInteger = 9999999999999998
dpl10 = 15
)
var (
maxDigits int
zeroArray []int
bigZero *BigInt
bigOne *BigInt
lr10 *BigInt
highBitMasks = []int{0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800,
0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF}
lowBitMasks = []int{0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,
0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}
hexToChar = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'}
hexatrigesimalToChar = []byte{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'}
)
// BigInt represents a big integer
type BigInt struct {
digits []int
isNeg bool
}
// NewBigInt creates a new BigInt
func NewBigInt() *BigInt {
digits := make([]int, len(zeroArray))
copy(digits, zeroArray)
return &BigInt{
digits: digits,
isNeg: false,
}
}
// NewBigIntEmpty creates an empty BigInt
func NewBigIntEmpty() *BigInt {
return &BigInt{
digits: nil,
isNeg: false,
}
}
// SetMaxDigits sets the maximum number of digits
func SetMaxDigits(value int) {
maxDigits = value
zeroArray = make([]int, maxDigits)
bigZero = NewBigInt()
bigOne = NewBigInt()
bigOne.digits[0] = 1
lr10 = BiFromNumber(1000000000000000)
}
// BiFromNumber creates a BigInt from a number
func BiFromNumber(i int64) *BigInt {
result := NewBigInt()
result.isNeg = i < 0
i = int64(math.Abs(float64(i)))
j := 0
for i > 0 {
result.digits[j] = int(i & maxDigitVal)
j++
i = i / biRadix
}
return result
}
// BiFromDecimal creates a BigInt from a decimal string
func BiFromDecimal(s string) *BigInt {
isNeg := len(s) > 0 && s[0] == '-'
i := 0
if isNeg {
i = 1
}
// Skip leading zeros
for i < len(s) && s[i] == '0' {
i++
}
if i == len(s) {
return NewBigInt()
}
digitCount := len(s) - i
fgl := digitCount % dpl10
if fgl == 0 {
fgl = dpl10
}
num := parseNumber(s[i : i+fgl])
result := BiFromNumber(num)
i += fgl
for i < len(s) {
result = BiAdd(BiMultiply(result, lr10), BiFromNumber(parseNumber(s[i:min(i+dpl10, len(s))])))
i += dpl10
}
result.isNeg = isNeg
return result
}
// BiCopy copies a BigInt
func BiCopy(bi *BigInt) *BigInt {
result := NewBigIntEmpty()
result.digits = make([]int, len(bi.digits))
copy(result.digits, bi.digits)
result.isNeg = bi.isNeg
return result
}
// ReverseStr reverses a string
func ReverseStr(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// BiToString converts BigInt to string with given radix
func BiToString(x *BigInt, radix int) string {
b := NewBigInt()
b.digits[0] = radix
qr := BiDivideModulo(x, b)
result := string(hexatrigesimalToChar[qr[1].digits[0]])
for BiCompare(qr[0], bigZero) == 1 {
qr = BiDivideModulo(qr[0], b)
result += string(hexatrigesimalToChar[qr[1].digits[0]])
}
prefix := ""
if x.isNeg {
prefix = "-"
}
return prefix + ReverseStr(result)
}
// BiToDecimal converts BigInt to decimal string
func BiToDecimal(x *BigInt) string {
b := NewBigInt()
b.digits[0] = 10
qr := BiDivideModulo(x, b)
result := fmt.Sprintf("%d", qr[1].digits[0])
for BiCompare(qr[0], bigZero) == 1 {
qr = BiDivideModulo(qr[0], b)
result += fmt.Sprintf("%d", qr[1].digits[0])
}
prefix := ""
if x.isNeg {
prefix = "-"
}
return prefix + ReverseStr(result)
}
// DigitToHex converts a digit to hex string
func DigitToHex(n int) string {
mask := 0xf
result := ""
for i := 0; i < 4; i++ {
result += string(hexToChar[n&mask])
n >>= 4
}
return ReverseStr(result)
}
// BiToHex converts BigInt to hex string
func BiToHex(x *BigInt) string {
result := ""
for i := BiHighIndex(x); i > -1; i-- {
result += DigitToHex(x.digits[i])
}
return result
}
// CharToHex converts a character code to hex value
func CharToHex(c byte) int {
const (
ZERO = 48
NINE = ZERO + 9
littleA = 97
littleZ = littleA + 25
bigA = 65
bigZ = 65 + 25
)
if c >= ZERO && c <= NINE {
return int(c - ZERO)
} else if c >= bigA && c <= bigZ {
return 10 + int(c-bigA)
} else if c >= littleA && c <= littleZ {
return 10 + int(c-littleA)
}
return 0
}
// HexToDigit converts hex string to digit
func HexToDigit(s string) int {
result := 0
sl := min(len(s), 4)
for i := 0; i < sl; i++ {
result <<= 4
result |= CharToHex(s[i])
}
return result
}
// BiFromHex creates BigInt from hex string
func BiFromHex(s string) *BigInt {
result := NewBigInt()
sl := len(s)
for i, j := sl, 0; i > 0; i, j = i-4, j+1 {
start := max(i-4, 0)
length := min(i, 4)
if start+length > sl {
length = sl - start
}
result.digits[j] = HexToDigit(s[start : start+length])
}
return result
}
// BiFromString creates BigInt from string with given radix
func BiFromString(s string, radix int) *BigInt {
isNeg := len(s) > 0 && s[0] == '-'
istop := 0
if isNeg {
istop = 1
}
result := NewBigInt()
place := NewBigInt()
place.digits[0] = 1
for i := len(s) - 1; i >= istop; i-- {
c := s[i]
digit := CharToHex(c)
biDigit := BiMultiplyDigit(place, digit)
result = BiAdd(result, biDigit)
place = BiMultiplyDigit(place, radix)
}
result.isNeg = isNeg
return result
}
// BiAdd adds two BigInts
func BiAdd(x, y *BigInt) *BigInt {
var result *BigInt
if x.isNeg != y.isNeg {
y.isNeg = !y.isNeg
result = BiSubtract(x, y)
y.isNeg = !y.isNeg
} else {
result = NewBigInt()
c := 0
for i := 0; i < len(x.digits); i++ {
n := x.digits[i] + y.digits[i] + c
result.digits[i] = n % biRadix
if n >= biRadix {
c = 1
} else {
c = 0
}
}
result.isNeg = x.isNeg
}
return result
}
// BiSubtract subtracts two BigInts
func BiSubtract(x, y *BigInt) *BigInt {
var result *BigInt
if x.isNeg != y.isNeg {
y.isNeg = !y.isNeg
result = BiAdd(x, y)
y.isNeg = !y.isNeg
} else {
result = NewBigInt()
c := 0
for i := 0; i < len(x.digits); i++ {
n := x.digits[i] - y.digits[i] + c
result.digits[i] = n % biRadix
if result.digits[i] < 0 {
result.digits[i] += biRadix
}
if n < 0 {
c = -1
} else {
c = 0
}
}
if c == -1 {
c = 0
for i := 0; i < len(x.digits); i++ {
n := 0 - result.digits[i] + c
result.digits[i] = n % biRadix
if result.digits[i] < 0 {
result.digits[i] += biRadix
}
if n < 0 {
c = -1
} else {
c = 0
}
}
result.isNeg = !x.isNeg
} else {
result.isNeg = x.isNeg
}
}
return result
}
// BiHighIndex returns the highest non-zero digit index
func BiHighIndex(x *BigInt) int {
result := len(x.digits) - 1
for result > 0 && x.digits[result] == 0 {
result--
}
return result
}
// BiNumBits returns the number of bits
func BiNumBits(x *BigInt) int {
n := BiHighIndex(x)
d := x.digits[n]
m := (n + 1) * bitsPerDigit
result := m
for result > m-bitsPerDigit {
if (d & 0x8000) != 0 {
break
}
d <<= 1
result--
}
return result
}
// BiMultiply multiplies two BigInts
func BiMultiply(x, y *BigInt) *BigInt {
result := NewBigInt()
n := BiHighIndex(x)
t := BiHighIndex(y)
for i := 0; i <= t; i++ {
c := 0
k := i
for j := 0; j <= n; j, k = j+1, k+1 {
uv := result.digits[k] + x.digits[j]*y.digits[i] + c
result.digits[k] = uv & maxDigitVal
c = uv >> biRadixBits
}
result.digits[i+n+1] = c
}
result.isNeg = x.isNeg != y.isNeg
return result
}
// BiMultiplyDigit multiplies BigInt by a digit
func BiMultiplyDigit(x *BigInt, y int) *BigInt {
result := NewBigInt()
n := BiHighIndex(x)
c := 0
for j := 0; j <= n; j++ {
uv := result.digits[j] + x.digits[j]*y + c
result.digits[j] = uv & maxDigitVal
c = uv >> biRadixBits
}
result.digits[1+n] = c
return result
}
// ArrayCopy copies array elements
func ArrayCopy(src []int, srcStart int, dest []int, destStart int, n int) {
m := min(srcStart+n, len(src))
for i, j := srcStart, destStart; i < m; i, j = i+1, j+1 {
dest[j] = src[i]
}
}
// BiShiftLeft shifts BigInt left by n bits
func BiShiftLeft(x *BigInt, n int) *BigInt {
digitCount := n / bitsPerDigit
result := NewBigInt()
ArrayCopy(x.digits, 0, result.digits, digitCount, len(result.digits)-digitCount)
bits := n % bitsPerDigit
rightBits := bitsPerDigit - bits
for i, i1 := len(result.digits)-1, len(result.digits)-2; i > 0; i, i1 = i-1, i1-1 {
result.digits[i] = ((result.digits[i] << bits) & maxDigitVal) |
((result.digits[i1] & highBitMasks[bits]) >> rightBits)
}
result.digits[0] = ((result.digits[0] << bits) & maxDigitVal)
result.isNeg = x.isNeg
return result
}
// BiShiftRight shifts BigInt right by n bits
func BiShiftRight(x *BigInt, n int) *BigInt {
digitCount := n / bitsPerDigit
result := NewBigInt()
ArrayCopy(x.digits, digitCount, result.digits, 0, len(x.digits)-digitCount)
bits := n % bitsPerDigit
leftBits := bitsPerDigit - bits
for i, i1 := 0, 1; i < len(result.digits)-1; i, i1 = i+1, i1+1 {
result.digits[i] = (result.digits[i] >> bits) |
((result.digits[i1] & lowBitMasks[bits]) << leftBits)
}
result.digits[len(result.digits)-1] >>= bits
result.isNeg = x.isNeg
return result
}
// BiMultiplyByRadixPower multiplies BigInt by radix^n
func BiMultiplyByRadixPower(x *BigInt, n int) *BigInt {
result := NewBigInt()
ArrayCopy(x.digits, 0, result.digits, n, len(result.digits)-n)
return result
}
// BiDivideByRadixPower divides BigInt by radix^n
func BiDivideByRadixPower(x *BigInt, n int) *BigInt {
result := NewBigInt()
ArrayCopy(x.digits, n, result.digits, 0, len(result.digits)-n)
return result
}
// BiModuloByRadixPower computes BigInt mod radix^n
func BiModuloByRadixPower(x *BigInt, n int) *BigInt {
result := NewBigInt()
ArrayCopy(x.digits, 0, result.digits, 0, n)
return result
}
// BiCompare compares two BigInts
func BiCompare(x, y *BigInt) int {
if x.isNeg != y.isNeg {
if x.isNeg {
return -1
}
return 1
}
for i := len(x.digits) - 1; i >= 0; i-- {
if x.digits[i] != y.digits[i] {
if x.isNeg {
if x.digits[i] > y.digits[i] {
return -1
}
return 1
} else {
if x.digits[i] < y.digits[i] {
return -1
}
return 1
}
}
}
return 0
}
// BiDivideModulo divides two BigInts and returns quotient and remainder
func BiDivideModulo(x, y *BigInt) []*BigInt {
nb := BiNumBits(x)
tb := BiNumBits(y)
origYIsNeg := y.isNeg
var q, r *BigInt
if nb < tb {
if x.isNeg {
q = BiCopy(bigOne)
q.isNeg = !y.isNeg
x.isNeg = false
y.isNeg = false
r = BiSubtract(y, x)
x.isNeg = true
y.isNeg = origYIsNeg
} else {
q = NewBigInt()
r = BiCopy(x)
}
return []*BigInt{q, r}
}
q = NewBigInt()
r = x
t := int(math.Ceil(float64(tb)/float64(bitsPerDigit))) - 1
lambda := 0
for y.digits[t] < biHalfRadix {
y = BiShiftLeft(y, 1)
lambda++
tb++
t = int(math.Ceil(float64(tb)/float64(bitsPerDigit))) - 1
}
r = BiShiftLeft(r, lambda)
nb += lambda
n := int(math.Ceil(float64(nb)/float64(bitsPerDigit))) - 1
b := BiMultiplyByRadixPower(y, n-t)
for BiCompare(r, b) != -1 {
q.digits[n-t]++
r = BiSubtract(r, b)
}
for i := n; i > t; i-- {
ri := 0
if i < len(r.digits) {
ri = r.digits[i]
}
ri1 := 0
if i-1 < len(r.digits) {
ri1 = r.digits[i-1]
}
ri2 := 0
if i-2 < len(r.digits) {
ri2 = r.digits[i-2]
}
yt := 0
if t < len(y.digits) {
yt = y.digits[t]
}
yt1 := 0
if t-1 < len(y.digits) {
yt1 = y.digits[t-1]
}
if ri == yt {
q.digits[i-t-1] = maxDigitVal
} else {
q.digits[i-t-1] = (ri*biRadix + ri1) / yt
}
c1 := q.digits[i-t-1] * ((yt * biRadix) + yt1)
c2 := (ri * biRadixSquared) + ((ri1 * biRadix) + ri2)
for c1 > c2 {
q.digits[i-t-1]--
c1 = q.digits[i-t-1] * ((yt * biRadix) | yt1)
c2 = (ri * biRadix * biRadix) + ((ri1 * biRadix) + ri2)
}
b = BiMultiplyByRadixPower(y, i-t-1)
r = BiSubtract(r, BiMultiplyDigit(b, q.digits[i-t-1]))
if r.isNeg {
r = BiAdd(r, b)
q.digits[i-t-1]--
}
}
r = BiShiftRight(r, lambda)
q.isNeg = x.isNeg != origYIsNeg
if x.isNeg {
if origYIsNeg {
q = BiAdd(q, bigOne)
} else {
q = BiSubtract(q, bigOne)
}
y = BiShiftRight(y, lambda)
r = BiSubtract(y, r)
}
if r.digits[0] == 0 && BiHighIndex(r) == 0 {
r.isNeg = false
}
return []*BigInt{q, r}
}
// BiDivide divides two BigInts
func BiDivide(x, y *BigInt) *BigInt {
return BiDivideModulo(x, y)[0]
}
// BiModulo computes x mod y
func BiModulo(x, y *BigInt) *BigInt {
return BiDivideModulo(x, y)[1]
}
// BiMultiplyMod computes (x * y) mod m
func BiMultiplyMod(x, y, m *BigInt) *BigInt {
return BiModulo(BiMultiply(x, y), m)
}
// BiPowMod computes x^y mod m
func BiPowMod(x, y, m *BigInt) *BigInt {
result := BiCopy(bigOne)
a := x
k := y
for {
if (k.digits[0] & 1) != 0 {
result = BiMultiplyMod(result, a, m)
}
k = BiShiftRight(k, 1)
if k.digits[0] == 0 && BiHighIndex(k) == 0 {
break
}
a = BiMultiplyMod(a, a, m)
}
return result
}
// BarrettMu represents Barrett reduction
type BarrettMu struct {
modulus *BigInt
k int
mu *BigInt
bkplus1 *BigInt
}
// NewBarrettMu creates a new BarrettMu
func NewBarrettMu(m *BigInt) *BarrettMu {
bm := &BarrettMu{
modulus: BiCopy(m),
k: BiHighIndex(m) + 1,
}
b2k := NewBigInt()
if 2*bm.k < len(b2k.digits) {
b2k.digits[2*bm.k] = 1
}
bm.mu = BiDivide(b2k, bm.modulus)
bm.bkplus1 = NewBigInt()
if bm.k+1 < len(bm.bkplus1.digits) {
bm.bkplus1.digits[bm.k+1] = 1
}
return bm
}
// Modulo performs Barrett modulo
func (bm *BarrettMu) Modulo(x *BigInt) *BigInt {
q1 := BiDivideByRadixPower(x, bm.k-1)
q2 := BiMultiply(q1, bm.mu)
q3 := BiDivideByRadixPower(q2, bm.k+1)
r1 := BiModuloByRadixPower(x, bm.k+1)
r2term := BiMultiply(q3, bm.modulus)
r2 := BiModuloByRadixPower(r2term, bm.k+1)
r := BiSubtract(r1, r2)
if r.isNeg {
r = BiAdd(r, bm.bkplus1)
}
for BiCompare(r, bm.modulus) >= 0 {
r = BiSubtract(r, bm.modulus)
}
return r
}
// MultiplyMod performs modular multiplication
func (bm *BarrettMu) MultiplyMod(x, y *BigInt) *BigInt {
xy := BiMultiply(x, y)
return bm.Modulo(xy)
}
// PowMod performs modular exponentiation
func (bm *BarrettMu) PowMod(x, y *BigInt) *BigInt {
result := NewBigInt()
result.digits[0] = 1
a := x
k := y
for {
if (k.digits[0] & 1) != 0 {
result = bm.MultiplyMod(result, a)
}
k = BiShiftRight(k, 1)
if k.digits[0] == 0 && BiHighIndex(k) == 0 {
break
}
a = bm.MultiplyMod(a, a)
}
return result
}
// NutRSAKeyPair represents an RSA key pair
type NutRSAKeyPair struct {
e *BigInt
d *BigInt
m *BigInt
chunkSize int
radix int
barrett *BarrettMu
}
// NewNutRSAKeyPair creates a new RSA key pair
func NewNutRSAKeyPair(encryptionExponent, decryptionExponent, modulus string) *NutRSAKeyPair {
kp := &NutRSAKeyPair{
e: BiFromHex(encryptionExponent),
d: BiFromHex(decryptionExponent),
m: BiFromHex(modulus),
radix: 16,
}
kp.chunkSize = 2 * BiHighIndex(kp.m)
kp.barrett = NewBarrettMu(kp.m)
return kp
}
// EncryptedString encrypts a string
func (kp *NutRSAKeyPair) EncryptedString(s string) string {
a := make([]int, 0)
for i := 0; i < len(s); i++ {
a = append(a, int(s[i]))
}
// Padding
for len(a)%kp.chunkSize != 0 {
a = append(a, 0)
}
result := ""
for i := 0; i < len(a); i += kp.chunkSize {
block := NewBigInt()
j := 0
for k := i; k < i+kp.chunkSize; j++ {
if k < len(a) {
block.digits[j] = a[k]
k++
}
if k < len(a) {
block.digits[j] += a[k] << 8
k++
}
}
crypt := kp.barrett.PowMod(block, kp.e)
var text string
if kp.radix == 16 {
text = BiToHex(crypt)
} else {
text = BiToString(crypt, kp.radix)
}
result += text + " "
}
if len(result) > 0 {
return result[:len(result)-1]
}
return result
}
// DecryptedString decrypts a string
func (kp *NutRSAKeyPair) DecryptedString(s string) string {
blocks := strings.Split(s, " ")
result := ""
for i := 0; i < len(blocks); i++ {
var bi *BigInt
if kp.radix == 16 {
bi = BiFromHex(blocks[i])
} else {
bi = BiFromString(blocks[i], kp.radix)
}
block := kp.barrett.PowMod(bi, kp.d)
for j := 0; j <= BiHighIndex(block); j++ {
result += string(rune(block.digits[j] & 255))
result += string(rune(block.digits[j] >> 8))
}
}
// Remove trailing null
if len(result) > 0 && result[len(result)-1] == 0 {
result = result[:len(result)-1]
}
return result
}
// Helper functions
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func parseNumber(s string) int64 {
var result int64
fmt.Sscanf(s, "%d", &result)
return result
}
func init() {
SetMaxDigits(130)
}
@@ -0,0 +1,153 @@
package cryptoutil
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
)
// RSAKeyPair RSA密钥对结构体
type RSAKeyPair struct {
n *big.Int // 模数
e *big.Int // 公钥指数
}
// NewRSAKeyPair 创建RSA密钥对
// exponent: 公钥指数的十六进制字符串,通常是 "10001" (65537)
// privateExponent: 私钥指数的十六进制字符串,加密时可以为空
// modulus: 模数的十六进制字符串
func NewRSAKeyPair(exponent, privateExponent, modulus string) (*RSAKeyPair, error) {
// 解析公钥指数
e := new(big.Int)
if _, ok := e.SetString(exponent, 16); !ok {
return nil, fmt.Errorf("invalid exponent: %s", exponent)
}
// 解析模数
n := new(big.Int)
if _, ok := n.SetString(modulus, 16); !ok {
return nil, fmt.Errorf("invalid modulus: %s", modulus)
}
return &RSAKeyPair{
n: n,
e: e,
}, nil
}
// NewRSAKeyPairFromPublicKey 从 X.509 格式的 base64 公钥字符串创建 RSA 密钥对
// publicKeyBase64: X.509 DER 格式的 base64 编码公钥字符串
func NewRSAKeyPairFromPublicKey(publicKeyBase64 string) (*RSAKeyPair, error) {
// 解码 base64
derBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
if err != nil {
return nil, fmt.Errorf("failed to decode base64: %w", err)
}
// 解析 X.509 格式的公钥
pubKey, err := x509.ParsePKIXPublicKey(derBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
// 转换为 RSA 公钥
rsaPubKey, ok := pubKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("not an RSA public key")
}
// 创建密钥对
return &RSAKeyPair{
n: rsaPubKey.N,
e: big.NewInt(int64(rsaPubKey.E)),
}, nil
}
// Encrypt 使用公钥加密数据,返回十六进制字符串
func (k *RSAKeyPair) Encrypt(plaintext string) (string, error) {
ciphertext, err := k.EncryptBytes([]byte(plaintext))
if err != nil {
return "", err
}
// 返回十六进制编码的密文
return hex.EncodeToString(ciphertext), nil
}
// EncryptBytes 使用公钥加密字节数据,返回加密后的字节
// 使用 PKCS1v15 填充方式,兼容 JavaScript RSA 实现
func (k *RSAKeyPair) EncryptBytes(plaintext []byte) ([]byte, error) {
// 计算密钥长度(字节)
keySize := (k.n.BitLen() + 7) / 8
// PKCS1v15 填充
padded, err := pkcs1v15Pad(plaintext, keySize)
if err != nil {
return nil, err
}
// 将填充后的数据转换为大整数
m := new(big.Int).SetBytes(padded)
// 执行 RSA 加密: c = m^e mod n
c := new(big.Int).Exp(m, k.e, k.n)
// 将结果转换为字节数组,确保长度与密钥长度一致
ciphertext := c.Bytes()
if len(ciphertext) < keySize {
// 如果结果长度不足,在前面填充零
result := make([]byte, keySize)
copy(result[keySize-len(ciphertext):], ciphertext)
return result, nil
}
return ciphertext, nil
}
// pkcs1v15Pad 实现 PKCS#1 v1.5 填充
func pkcs1v15Pad(data []byte, keySize int) ([]byte, error) {
if len(data) > keySize-11 {
return nil, fmt.Errorf("data too long: %d bytes (max %d)", len(data), keySize-11)
}
// PKCS1v15 格式: 0x00 || 0x02 || PS || 0x00 || data
// 其中 PS 是随机非零字节,长度至少为 8 字节
padLen := keySize - len(data) - 3
padded := make([]byte, keySize)
padded[0] = 0x00
padded[1] = 0x02
// 生成随机非零字节作为填充
ps := make([]byte, padLen)
_, err := rand.Read(ps)
if err != nil {
return nil, fmt.Errorf("failed to generate random padding: %w", err)
}
// 确保所有填充字节都是非零
for i := 0; i < padLen; i++ {
for ps[i] == 0 {
rand.Read(ps[i : i+1])
}
padded[2+i] = ps[i]
}
padded[2+padLen] = 0x00
copy(padded[3+padLen:], data)
return padded, nil
}
// GetModulus 获取模数
func (k *RSAKeyPair) GetModulus() *big.Int {
return k.n
}
// GetExponent 获取公钥指数
func (k *RSAKeyPair) GetExponent() *big.Int {
return k.e
}
@@ -0,0 +1,87 @@
package cryptoutil
import (
"testing"
)
func TestRSAKeyPair(t *testing.T) {
// 使用你提供的参数创建 RSA 密钥对
exponent := "10001"
modulus := "a1155dc73de04614604cbed62cc3b3198a639eb7b7d19cc2dfe3e787fe435f1464beee42a9a3cab6f1d3bfe66cd00317220ee770fd779b24c12c7698e251e875"
keyPair, err := NewRSAKeyPair(exponent, "", modulus)
if err != nil {
t.Fatalf("创建 RSA 密钥对失败: %v", err)
}
// 测试加密
plaintext := "241703070163"
ciphertext, err := keyPair.Encrypt(plaintext)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
t.Logf("明文: %s", plaintext)
t.Logf("密文(十六进制): %s", ciphertext)
// 验证密文不为空
if len(ciphertext) == 0 {
t.Error("密文为空")
}
}
func TestRSAKeyPair_EncryptBytes(t *testing.T) {
exponent := "10001"
modulus := "a1155dc73de04614604cbed62cc3b3198a639eb7b7d19cc2dfe3e787fe435f1464beee42a9a3cab6f1d3bfe66cd00317220ee770fd779b24c12c7698e251e875"
keyPair, err := NewRSAKeyPair(exponent, "", modulus)
if err != nil {
t.Fatalf("创建 RSA 密钥对失败: %v", err)
}
// 测试字节加密
plaintext := []byte("Test data 测试数据")
ciphertext, err := keyPair.EncryptBytes(plaintext)
if err != nil {
t.Fatalf("字节加密失败: %v", err)
}
if len(ciphertext) == 0 {
t.Error("密文字节为空")
}
t.Logf("密文长度: %d bytes", len(ciphertext))
}
func TestNewRSAKeyPairFromPublicKey(t *testing.T) {
// 使用你提供的 X.509 格式的 base64 公钥
publicKey := "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCpbRy8ZoyQvRPpUDIXycglTwVDcYrLcv7P9HI4E7/TAPZiI3GN0ckTUVVWRvoo/re3uCOOJK+F+Ufh19XiRIdoPYCULsSyEjcLLiyAS09mVOzhAQco84E/lM24T7rRTVID7LIgWPewyN8Nd5vzHET4Q3qTthoL8n82BwC1iXuVQIDAQAB"
keyPair, err := NewRSAKeyPairFromPublicKey(publicKey)
if err != nil {
t.Fatalf("从公钥创建 RSA 密钥对失败: %v", err)
}
// 测试加密
plaintext := "241703070163"
ciphertext, err := keyPair.Encrypt(plaintext)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
t.Logf("明文: %s", plaintext)
t.Logf("密文(十六进制): %s", ciphertext)
// 验证密文不为空
if len(ciphertext) == 0 {
t.Error("密文为空")
}
// 验证密文长度(1024位密钥应该产生128字节 = 256个十六进制字符)
expectedHexLen := (keyPair.GetModulus().BitLen() + 7) / 8 * 2
if len(ciphertext) != expectedHexLen {
t.Errorf("密文长度不正确: 期望 %d, 实际 %d", expectedHexLen, len(ciphertext))
}
t.Logf("密钥长度: %d bits", keyPair.GetModulus().BitLen())
}
+209
View File
@@ -0,0 +1,209 @@
package login
import (
"bytes"
"context"
"fmt"
"io"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"github.com/PuerkitoBio/goquery"
"github.com/samber/oops"
"resty.dev/v3"
)
// 南阳理工学院登录实现 RSA加密 & 验证码必出
type CpdailyAhjzuLogin struct {
client *resty.Client
}
func (c CpdailyAhjzuLogin) Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) {
jar := cookiejar.NewPersistCookieJar()
c.client.SetCookieJar(jar).SetRedirectPolicy(resty.NoRedirectPolicy())
// 获取合法的登录URL
loginUrl := getCasValidLoginUrl(schoolDetail)
// 请求登录页面并获取真正的登录URL
loginPageResponse := getAhjzuLoginPageResponse(ctx, c.client, loginUrl)
defer loginPageResponse.Body.Close()
loginPageBytes, err := io.ReadAll(loginPageResponse.Body)
utils.OkOrPanic(err)
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
// 解析登录页面
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
utils.OkOrPanic(err)
// 判断登录类型
var (
allInputs = map[string]string{}
fm1Node = loginPage.Find("#credentials").First()
)
inputs := fm1Node.Find("input")
for _, input := range inputs.EachIter() {
name, exists := input.Attr("name")
if !exists {
continue
}
value, _ := input.Attr("value")
allInputs[name] = value
}
allInputs["username"] = username
// 处理密码
allInputs["password"] = cryptoutil.MD5(password)
// 解决常规图片验证码
captchaUrl := getAhjzuCaptchaUrl(loginResponseUrl)
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
allInputs["validateCode"] = captchaResult
// 登录提交
loginPostResponse, err := c.client.R().
SetFormData(allInputs).
WithContext(ctx).
Post(loginResponseUrl.String())
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
defer loginPostResponse.Body.Close()
switch loginPostResponse.StatusCode() {
case 302:
handleCasLoginSuccess(ctx, loginPostResponse, schoolDetail, username, password, c.client)
case 200:
fallthrough
case 401:
doc, err := goquery.NewDocumentFromReader(loginPostResponse.Body)
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
if isCasNeedKickOut(doc) {
kickoutResponse := submitCasKickOut(ctx, loginResponseUrl.String(), doc, c.client)
handleCasLoginSuccess(ctx, kickoutResponse, schoolDetail, username, password, c.client)
return
}
message := getCasErrorMessage(doc)
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("今日校园登录失败: %s", message)), func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
With("errorMessage", message).
Wrap(errors.NewBusinessError(503, message))
})
}
}
func getAhjzuLoginPageResponse(ctx context.Context, client *resty.Client, loginUrl string) *resty.Response {
res1, err := client.R().Get(loginUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("loginUrl", loginUrl).
WithContext(ctx).
Wrap(err)
})
if res1.StatusCode() != http.StatusFound && res1.StatusCode() != http.StatusMovedPermanently {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园登录失败: 登录页面获取失败"), func(err error) error {
return oops.
With("loginUrl", loginUrl).
With("statusCode", res1.StatusCode()).
WithContext(ctx).
Wrap(err)
})
}
location := res1.RawResponse.Header.Get("Location")
res2, err := client.R().Get(location)
if res2.StatusCode() != http.StatusFound && res2.StatusCode() != http.StatusMovedPermanently {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园登录失败: 登录页面获取失败"), func(err error) error {
return oops.
With("loginUrl", loginUrl).
With("statusCode", res2.StatusCode()).
WithContext(ctx).
Wrap(err)
})
}
location = res2.RawResponse.Header.Get("Location")
res3, err := client.R().Get(location)
if res3.StatusCode() != http.StatusFound && res3.StatusCode() != http.StatusMovedPermanently {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园登录失败: 登录页面获取失败"), func(err error) error {
return oops.
With("loginUrl", loginUrl).
With("statusCode", res3.StatusCode()).
WithContext(ctx).
Wrap(err)
})
}
location = res3.RawResponse.Header.Get("Location")
res4, err := client.R().Get(location)
if res4.StatusCode() != http.StatusFound && res3.StatusCode() != http.StatusMovedPermanently {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园登录失败: 登录页面获取失败"), func(err error) error {
return oops.
With("loginUrl", loginUrl).
With("statusCode", res4.StatusCode()).
WithContext(ctx).
Wrap(err)
})
}
location = res4.RawResponse.Header.Get("Location")
res5, err := client.R().Get(location)
if err != nil {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园登录失败: 登录页面获取失败"), func(err error) error {
return oops.
With("loginUrl", loginUrl).
With("statusCode", res4.StatusCode()).
WithContext(ctx).
Wrap(err)
})
}
return res5
}
func getAhjzuCaptchaUrl(url *url.URL) string {
return fmt.Sprintf("%s://%s/sso/code/code.jsp", url.Scheme, url.Host)
}
func (c CpdailyAhjzuLogin) GetCookieJar() http.CookieJar {
return c.client.CookieJar()
}
func (c CpdailyAhjzuLogin) GetCookieString() string {
cookieJar := c.GetCookieJar()
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
return serializableJar.Serialize()
}
func (c CpdailyAhjzuLogin) GetClient() *resty.Client {
return c.client
}
func NewAhjzu(ctx context.Context) *CpdailyAhjzuLogin {
return &CpdailyAhjzuLogin{
client: httpclient.NewCpdailyClient(),
}
}
@@ -7,6 +7,18 @@ import (
func GetCpdailyLoginBySchoolInfo(ctx context.Context, info *school.SchoolDetailInfo) CpdailyLogin {
switch info.Id {
// 南昌理工学院
case "d6e8f2ca-10b2-4599-bdb8-4b33d419658d":
return NewNut(ctx)
// 安徽建筑大学
case "ahjzu":
return NewAhjzu(ctx)
// 南宁师范大学
case "2ba0ea59-a764-4482-bb2b-c924b726b04c":
return NewNnnu(ctx)
// 河北石油职业技术大学
case "01e270d1-00e2-4ce9-b0af-5d5fbd462b8a":
return NewIap(ctx)
default:
if info.IsIap() {
return NewIap(ctx)
+58 -19
View File
@@ -16,11 +16,14 @@ import (
"net/http"
"net/url"
"regexp"
"slices"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/avast/retry-go/v4"
"github.com/deatil/go-cryptobin/cryptobin/crypto"
"github.com/gogf/gf/v2/frame/g"
"github.com/samber/oops"
"github.com/tidwall/gjson"
"resty.dev/v3"
@@ -37,7 +40,7 @@ type CpdailyCasLogin struct {
func NewCas(ctx context.Context) CpdailyLogin {
return &CpdailyCasLogin{
client: httpclient.Cpdaily.Clone(ctx),
client: httpclient.NewCpdailyClient(),
}
}
@@ -56,7 +59,7 @@ func (c *CpdailyCasLogin) GetClient() *resty.Client {
func (c *CpdailyCasLogin) Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) {
jar := cookiejar.NewPersistCookieJar()
c.client = c.client.SetCookieJar(jar).SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
c.client.SetCookieJar(jar).SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
// 获取合法的登录URL
loginUrl := getCasValidLoginUrl(schoolDetail)
@@ -76,7 +79,7 @@ func (c *CpdailyCasLogin) Login(ctx context.Context, schoolDetail *school.School
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
// 关闭client的重定向
c.client = c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
// 解析登录页面
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
@@ -155,11 +158,11 @@ func (c *CpdailyCasLogin) Login(ctx context.Context, schoolDetail *school.School
allInputs[name] = value
}
} else {
utils.OkOrPanic(oops.With("schoolDetail", schoolDetail).
utils.OkOrPanic(errors.NewBusinessError(503, "页面中未找到登录表单"), oops.With("schoolDetail", schoolDetail).
With("username", username).
With("password", password).
WithContext(ctx).
Wrap(errors.NewBusinessError(503, "页面中未找到登录表单")))
Wrap)
}
// 修改部分参数
@@ -189,19 +192,37 @@ func (c *CpdailyCasLogin) Login(ctx context.Context, schoolDetail *school.School
allInputs["password"] = encryptCasPassword(password, salt)
}
// 声明需要滑块验证码的学校id
var SliderCaptchaIds = g.Cfg().MustGet(ctx, cst.ConfigSliderSchoolIds).Strings()
// []string{"1018764144934965", "sdhyxy", "hqdx"}
// 判断是否需要验证码
if isCasNeedCaptcha(ctx, loginResponseUrl, loginType, username, c.client) {
// 解决验证码
captchaUrl := getCasCaptchaUrl(loginResponseUrl, loginType)
// 如果是滑块验证码
if slices.Contains(SliderCaptchaIds, schoolDetail.Id) {
e := retry.Do(func() error {
return utils.Catch(func() {
captchaUrl := getCasSliderCaptchaUrl(loginResponseUrl)
verifyUrl := getCasSliderVerifyUrl(loginResponseUrl)
ResolveCasSliderCaptcha(ctx, captchaUrl, verifyUrl, c.client)
})
}, retry.MaxDelay(15*time.Second), retry.Attempts(5), retry.Context(ctx))
if e != nil {
utils.OkOrPanic(errors.NewBusinessError(503, "滑块验证码解析失败").WithInnerError(e), oops.WithContext(ctx).Wrap)
}
} else {
// 解决常规图片验证码
captchaUrl := getCasCaptchaUrl(loginResponseUrl, loginType)
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
switch loginType {
case 0:
allInputs["captchaResponse"] = captchaResult
default:
allInputs["captcha"] = captchaResult
switch loginType {
case 0:
allInputs["captchaResponse"] = captchaResult
default:
allInputs["captcha"] = captchaResult
}
}
}
loginPostResponse, err := c.client.R().
@@ -270,7 +291,7 @@ func (c *CpdailyCasLogin) ConfirmAdd(ctx context.Context, sc *school.SchoolDetai
utils.OkOrPanic(err)
// 提交确认添加
client := httpclient.Cpdaily.Clone(ctx)
client := httpclient.NewCpdailyClient()
client.SetCookieJar(cookieJar)
post, err := client.R().SetFormData(map[string]string{
"service": service,
@@ -334,7 +355,7 @@ func handleCasLoginSuccess(ctx context.Context, response *resty.Response, school
jumpUrl, err := response.RawResponse.Location()
utils.OkOrPanic(err)
client = client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
verifyResponse, err := client.R().
WithContext(ctx).
@@ -370,6 +391,10 @@ func handleCasLoginSuccess(ctx context.Context, response *resty.Response, school
utils.OkOrPanic(errors.NewBusinessError(201, "多因子验证码认证").WithData(key))
// 否则应该是登录成功
} else if statusCode == 200 || statusCode == 404 {
all, _ := io.ReadAll(verifyResponse.Body)
if strings.Contains(string(all), "完善资料") || strings.Contains(string(all), "为了你的帐号安全") {
utils.OkOrPanic(errors.NewBusinessError(503, "请手动学工号登录完善信息,修改密码").WithData(schoolInfo.WecloudUrl))
}
ActivateSession(ctx, schoolInfo.WecloudUrl, client)
} else {
utils.OkOrPanic(errors.NewBusinessError(503, "未知错误导致今日校园登录失败"),
@@ -399,7 +424,7 @@ func submitCasKickOut(ctx context.Context, requestUrl string, doc *goquery.Docum
submitForm[name] = value
}
client = client.SetRedirectPolicy(resty.NoRedirectPolicy())
client.SetRedirectPolicy(resty.NoRedirectPolicy())
response, err := client.R().
SetFormData(submitForm).
@@ -431,11 +456,16 @@ func submitCasKickOut(ctx context.Context, requestUrl string, doc *goquery.Docum
}
func getCasValidLoginUrl(info *school.SchoolDetailInfo) string {
if strings.Contains(info.AmpUrl, "campusphere") {
// 特殊学校单独处理
// 甘肃农业大学
if info.Id == "gsau" {
return info.IdsUrl
}
if strings.Contains(info.AmpUrl, "campusphere") || strings.Contains(info.AmpUrl, "wec-portal-mobile") {
return info.AmpUrl
} else if strings.Contains(info.AmpUrl2, "campusphere") {
} else if strings.Contains(info.AmpUrl2, "campusphere") || strings.Contains(info.AmpUrl2, "wec-portal-mobile") {
return info.AmpUrl2
} else if strings.Contains(info.AmpUrl3, "campusphere") {
} else if strings.Contains(info.AmpUrl3, "campusphere") || strings.Contains(info.AmpUrl3, "wec-portal-mobile") {
return info.AmpUrl3
}
@@ -529,3 +559,12 @@ func getCasCaptchaUrl(url *url.URL, loginType int) string {
return fmt.Sprintf("%s://%s/authserver/getCaptcha.htl", url.Scheme, url.Host)
}
}
func getCasSliderCaptchaUrl(url *url.URL) string {
// /authserver/common/openSliderCaptcha.htl?_=1762873725346
return fmt.Sprintf("%s://%s/authserver/common/openSliderCaptcha.htl", url.Scheme, url.Host)
}
func getCasSliderVerifyUrl(url *url.URL) string {
return fmt.Sprintf("%s://%s/authserver/common/verifySliderCaptcha.htl", url.Scheme, url.Host)
}
@@ -0,0 +1,51 @@
package login
import (
"context"
"encoding/base64"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/httpclient"
"github.com/bytedance/sonic"
"resty.dev/v3"
)
type CpdailyCodeLogin struct {
client *resty.Client
}
func NewCode(ctx context.Context) *CpdailyCodeLogin {
return &CpdailyCodeLogin{
client: httpclient.NewCpdailyClient(),
}
}
func (c *CpdailyCodeLogin) GetLoginCode(mobile string) {
mobileBytes := cryptoutil.AesEncrypt([]byte(mobile))
mobile = base64.StdEncoding.EncodeToString(mobileBytes)
c.client.R().SetBody(map[string]string{
"a": mobile,
"b": "first_v4",
}).Post("https://mobile.campushoy.com/app/auth/authentication/mobile/messageCode/v-8222")
}
func (c *CpdailyCodeLogin) LoginByCode(mobile, code string) {
type loginStruct struct {
C string `json:"c"`
D string `json:"d"`
E string `json:"e"`
F string `json:"f"`
}
data := loginStruct{
C: mobile,
D: code,
E: "CPDAILY",
F: "86",
}
marshal, _ := sonic.Marshal(data)
marshalBytes := cryptoutil.AesEncrypt(marshal)
c.client.R().SetBody(map[string]string{
"a": base64.StdEncoding.EncodeToString(marshalBytes),
"b": "first_v4",
}).Post("https://mobile.campushoy.com/app/auth/authentication/mobileLogin/v-8222")
}
+13 -16
View File
@@ -14,7 +14,12 @@ import (
)
func ActivateSession(ctx context.Context, host string, client *resty.Client) {
client = client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
// 处理cookie,干掉path
jar := client.CookieJar()
realJar := jar.(*cookiejar.PersistCookieJar)
realJar.RemoveCookieInfo()
client.SetCookieJar(realJar)
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
host = strings.TrimSuffix(host, "/")
@@ -27,7 +32,7 @@ func ActivateSession(ctx context.Context, host string, client *resty.Client) {
defer sessionResponse.Body.Close()
if !sessionResponse.IsSuccess() {
if !sessionResponse.IsSuccess() && sessionResponse.StatusCode() != 502 {
utils.OkOrPanic(errors.NewBusinessError(503, "Session激活失败"),
func(err error) error {
return oops.
@@ -78,34 +83,26 @@ func ActivateSession(ctx context.Context, host string, client *resty.Client) {
activateStep1()
activateStep2()
client = client.SetRedirectPolicy(resty.NoRedirectPolicy())
client.SetRedirectPolicy(resty.NoRedirectPolicy())
}
func CookieEnabled(ctx context.Context, host string, client *resty.Client) bool {
client = client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
defer func() {
client = client.SetRedirectPolicy(resty.NoRedirectPolicy())
}()
client.SetRedirectPolicy(resty.NoRedirectPolicy())
host = strings.TrimSuffix(host, "/")
sessionResponse, err := client.R().
WithContext(ctx).
SetBody(map[string]any{}).
Post(fmt.Sprintf("%s/wec-counselor-attendance-apps/student/attendance/getStuSignInfosByWeekMonth", host))
Post(fmt.Sprintf("%s/wec-counselor-attendance-apps/student/attendance/getStuAttendacesInOneDay", host))
utils.OkOrPanic(err)
defer sessionResponse.Body.Close()
if !sessionResponse.IsSuccess() {
return sessionResponse.StatusCode() == 405
}
responseContentType := sessionResponse.Header().Get("Content-Type")
return strings.Contains(responseContentType, "json")
return sessionResponse.StatusCode() != 302
}
func GetClient(ctx context.Context, cookie string) *resty.Client {
cookieJar := cookiejar.NewPersistCookieJar()
cookieJar.Deserialize(cookie)
client := httpclient.Cpdaily.Clone(ctx)
// 不使用 Clone,创建全新的独立 client 实例避免状态共享
client := httpclient.NewCpdailyClient()
client.SetCookieJar(cookieJar)
return client
}
+44 -12
View File
@@ -5,25 +5,29 @@ import (
"fmt"
"io"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"strings"
"github.com/samber/oops"
"github.com/tidwall/gjson"
"resty.dev/v3"
)
const iapRsaPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCpbRy8ZoyQvRPpUDIXycglTwVDcYrLcv7P9HI4E7/TAPZiI3GN0ckTUVVWRvoo/re3uCOOJK+F+Ufh19XiRIdoPYCULsSyEjcLLiyAS09mVOzhAQco84E/lM24T7rRTVID7LIgWPewyN8Nd5vzHET4Q3qTthoL8n82BwC1iXuVQIDAQAB"
type CpdailyIapLogin struct {
client *resty.Client
}
func NewIap(ctx context.Context) *CpdailyIapLogin {
return &CpdailyIapLogin{
client: httpclient.Cpdaily.Clone(ctx),
client: httpclient.NewCpdailyClient(),
}
}
@@ -34,14 +38,13 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
loginUrl := getIapValidLoginUrl(schoolDetail)
jar := cookiejar.NewPersistCookieJar()
c.client = c.client.SetCookieJar(jar).
c.client.SetCookieJar(jar).
SetRedirectPolicy(resty.NoRedirectPolicy())
ltInfoResponse, err := c.client.R().
WithContext(ctx).
SetBody(map[string]any{}).
Post(fmt.Sprintf("%s/iap/security/lt", loginUrl))
// 获取基础lt
preLoginUrl := loginUrl + "/portal/login"
c.client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
loginRedirectResponse, err := c.client.R().Get(preLoginUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
In("CpdailyIapLogin.Login").
@@ -50,6 +53,25 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
With("password", password).
Wrap(err)
})
u := loginRedirectResponse.RawResponse.Request.URL
oldLt := strings.Split(u.String(), "=")[1]
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
ltInfoResponse, err := c.client.R().
WithContext(ctx).
SetFormData(map[string]string{
"lt": oldLt,
}).
Post(fmt.Sprintf("%s/iap/security/lt", loginUrl))
rsa, _ := cryptoutil.NewRSAKeyPairFromPublicKey(iapRsaPubKey)
password, _ = rsa.Encrypt(password)
utils.OkOrPanic(err, func(err error) error {
return oops.
In("CpdailyIapLogin.Login").
With("schoolDetail", schoolDetail).
With("username", username).
With("password", "{rsa}"+password).
Wrap(err)
})
defer ltInfoResponse.Body.Close()
@@ -64,8 +86,9 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
"rememberMe": "true",
"dllt": "",
"mobile": "",
"captcha": "",
"username": username,
"password": password,
"password": "{rsa}" + password,
}
if isIapNeedCaptcha(ctx, loginUrl, username, c.client) {
@@ -103,7 +126,7 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
Wrap(err)
})
c.client = c.client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
c.client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
finalLoginResponse, err := c.client.R().
WithContext(ctx).
@@ -123,7 +146,7 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
ActivateSession(ctx, schoolDetail.WecloudUrl, c.client)
c.client = c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
default:
loginResponseBytes, err := io.ReadAll(loginResponse.Body)
utils.OkOrPanic(err, func(err error) error {
@@ -142,9 +165,18 @@ func (c *CpdailyIapLogin) Login(ctx context.Context, schoolDetail *school.School
case "CAPTCHA_NOTMATCH":
utils.OkOrPanic(errors.NewBusinessError(503, "Iap登录失败, 验证码识别失败"))
case "FAIL_UPNOTMATCH":
utils.OkOrPanic(errors.NewBusinessError(503, "Iap登录失败, 用户名或密码错误"))
utils.OkOrPanic(errors.NewBusinessError(503, "Iap登录失败, 用户名或密码错误"), func(err error) error {
return oops.With("username", username).
With("password", password).
With("userId", utils.GetUserIdNotPanic(ctx)).
In("Cpdaily.Login").New("Iap登录失败, 用户名或密码错误")
})
default:
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("Iap登录失败, 状态识别码: %s", resultCode)))
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("Iap登录失败, 状态识别码: %s", resultCode)),
oops.With("username", username).
With("password", password).
With("userId", utils.GetUserIdNotPanic(ctx)).
In("Cpdaily.Login").Wrap)
}
}
}
+149
View File
@@ -0,0 +1,149 @@
package login
import (
"bytes"
"context"
"fmt"
"io"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"github.com/PuerkitoBio/goquery"
"github.com/samber/oops"
"resty.dev/v3"
)
// 南宁师范大学登录实现
type CpdailyNnnuLogin struct {
client *resty.Client
}
func (c CpdailyNnnuLogin) Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) {
jar := cookiejar.NewPersistCookieJar()
c.client.SetCookieJar(jar).SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
// 获取合法的登录URL
loginUrl := getCasValidLoginUrl(schoolDetail)
// 请求登录页面并获取真正的登录URL
loginPageResponse, err := c.client.R().
WithContext(ctx).
Get(loginUrl)
defer loginPageResponse.Body.Close()
loginPageBytes, err := io.ReadAll(loginPageResponse.Body)
utils.OkOrPanic(err)
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
// 关闭client的重定向
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
// 解析登录页面
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
utils.OkOrPanic(err)
// 判断登录类型
var (
allInputs = map[string]string{}
form = loginPage.Find("form").First()
)
inputs := form.Find("input")
for _, input := range inputs.EachIter() {
name, exists := input.Attr("name")
if !exists {
continue
}
value, _ := input.Attr("value")
allInputs[name] = value
}
allInputs["username"] = username
// 处理密码
allInputs["password"] = cryptoutil.MD5(password)
// 解决常规图片验证码
captchaUrl := getAhjzuCaptchaUrl(loginResponseUrl)
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
allInputs["validateCode"] = captchaResult
// 登录提交
loginPostResponse, err := c.client.R().
SetFormData(allInputs).
WithContext(ctx).
Post(loginResponseUrl.String())
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
defer loginPostResponse.Body.Close()
switch loginPostResponse.StatusCode() {
case 302:
handleCasLoginSuccess(ctx, loginPostResponse, schoolDetail, username, password, c.client)
case 200:
fallthrough
case 401:
doc, err := goquery.NewDocumentFromReader(loginPostResponse.Body)
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
if isCasNeedKickOut(doc) {
kickoutResponse := submitCasKickOut(ctx, loginResponseUrl.String(), doc, c.client)
handleCasLoginSuccess(ctx, kickoutResponse, schoolDetail, username, password, c.client)
return
}
message := getCasErrorMessage(doc)
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("今日校园登录失败: %s", message)), func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
With("errorMessage", message).
Wrap(errors.NewBusinessError(503, message))
})
}
}
func getNuuuCaptchaUrl(url *url.URL) string {
return fmt.Sprintf("%s://%s/sso/code/code.jsp", url.Scheme, url.Host)
}
func (c CpdailyNnnuLogin) GetCookieJar() http.CookieJar {
return c.client.CookieJar()
}
func (c CpdailyNnnuLogin) GetCookieString() string {
cookieJar := c.GetCookieJar()
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
return serializableJar.Serialize()
}
func (c CpdailyNnnuLogin) GetClient() *resty.Client {
return c.client
}
func NewNnnu(ctx context.Context) CpdailyLogin {
return &CpdailyNnnuLogin{
client: httpclient.NewCpdailyClient(),
}
}
+211
View File
@@ -0,0 +1,211 @@
package login
import (
"bytes"
"context"
"fmt"
"io"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/bytedance/sonic"
"github.com/samber/oops"
"resty.dev/v3"
)
// 南阳理工学院登录实现 RSA加密 & 验证码必出
type CpdailyNutLogin struct {
client *resty.Client
}
func (c CpdailyNutLogin) Login(ctx context.Context, schoolDetail *school.SchoolDetailInfo, username, password string) {
jar := cookiejar.NewPersistCookieJar()
c.client.SetCookieJar(jar).SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
// 获取合法的登录URL
loginUrl := getCasValidLoginUrl(schoolDetail)
// 请求登录页面并获取真正的登录URL
loginPageResponse, err := c.client.R().
WithContext(ctx).
Get(loginUrl)
utils.OkOrPanic(err)
defer loginPageResponse.Body.Close()
loginPageBytes, err := io.ReadAll(loginPageResponse.Body)
utils.OkOrPanic(err)
loginResponseUrl := loginPageResponse.RawResponse.Request.URL
// 关闭client的重定向
c.client.SetRedirectPolicy(resty.NoRedirectPolicy())
// 解析登录页面
loginPage, err := goquery.NewDocumentFromReader(bytes.NewReader(loginPageBytes))
utils.OkOrPanic(err)
// 判断登录类型
var (
allInputs = map[string]string{}
fm1Node = loginPage.Find("#fm1").First()
)
inputs := fm1Node.Find("input")
for _, input := range inputs.EachIter() {
name, exists := input.Attr("name")
if !exists {
continue
}
value, _ := input.Attr("value")
allInputs[name] = value
}
allInputs["username"] = username
// 处理密码
type NutPubKeyResponse struct {
Modulus string `json:"modulus"`
Exponent string `json:"exponent"`
}
var pubKey NutPubKeyResponse
pubKeyResponse, err := c.client.R().Get(getNutPubKey(loginResponseUrl))
utils.OkOrPanic(err)
defer pubKeyResponse.Body.Close()
pubKeyBytes, err := io.ReadAll(pubKeyResponse.Body)
utils.OkOrPanic(err)
sonic.Unmarshal(pubKeyBytes, &pubKey)
rsa := cryptoutil.NewNutRSAKeyPair(pubKey.Exponent, "", pubKey.Modulus)
password = rsa.EncryptedString(reverseStringASCII(password))
allInputs["password"] = password
if isNutNeedCaptcha(ctx, loginResponseUrl, c.client) {
// 解决常规图片验证码
captchaUrl := getNutCaptchaUrl(loginResponseUrl)
captchaResult := ResolveOcrCaptcha(ctx, captchaUrl, c.client)
allInputs["authcode"] = captchaResult
} else {
allInputs["authcode"] = ""
}
// 登录提交
loginPostResponse, err := c.client.R().
SetFormData(allInputs).
WithContext(ctx).
Post(loginResponseUrl.String())
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
defer loginPostResponse.Body.Close()
switch loginPostResponse.StatusCode() {
case 302:
handleCasLoginSuccess(ctx, loginPostResponse, schoolDetail, username, password, c.client)
case 200:
fallthrough
case 401:
doc, err := goquery.NewDocumentFromReader(loginPostResponse.Body)
utils.OkOrPanic(err, func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
Wrap(err)
})
if isCasNeedKickOut(doc) {
kickoutResponse := submitCasKickOut(ctx, loginResponseUrl.String(), doc, c.client)
handleCasLoginSuccess(ctx, kickoutResponse, schoolDetail, username, password, c.client)
return
}
message := getCasErrorMessage(doc)
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("今日校园登录失败: %s", message)), func(err error) error {
return oops.
WithContext(ctx).
With("loginResponseUrl", loginResponseUrl).
With("allInputs", allInputs).
With("errorMessage", message).
Wrap(errors.NewBusinessError(503, message))
})
}
}
func getNutCaptchaUrl(url *url.URL) string {
return fmt.Sprintf("%s://%s/cas/kaptcha", url.Scheme, url.Host)
}
func isNutNeedCaptcha(ctx context.Context, url *url.URL, client *resty.Client) bool {
requestURL := getNutNeedCaptchaUrl(url)
captchaResponse, err := client.R().
WithContext(ctx).
Get(requestURL)
utils.OkOrPanic(oops.With().
With("requestURL", requestURL).
WithContext(ctx).
Wrap(err))
defer captchaResponse.Body.Close()
if captchaResponse.IsError() {
utils.OkOrPanic(oops.With().
With("requestURL", requestURL).
WithContext(ctx).
Wrap(errors.NewBusinessError(503, "请求验证码失败")))
}
responseBytes, err := io.ReadAll(captchaResponse.Body)
utils.OkOrPanic(err)
return strings.ToLower(strings.TrimSpace(string(responseBytes))) != "false"
}
func getNutPubKey(url *url.URL) string {
return fmt.Sprintf("%s://%s/cas/v2/getPubKey", url.Scheme, url.Host)
}
func getNutNeedCaptchaUrl(url *url.URL) string {
return fmt.Sprintf("%s://%s/cas/v2/getKaptchaStatus", url.Scheme, url.Host)
}
func (c CpdailyNutLogin) GetCookieJar() http.CookieJar {
return c.client.CookieJar()
}
func (c CpdailyNutLogin) GetCookieString() string {
cookieJar := c.GetCookieJar()
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
return serializableJar.Serialize()
}
func (c CpdailyNutLogin) GetClient() *resty.Client {
return c.client
}
func NewNut(ctx context.Context) *CpdailyNutLogin {
return &CpdailyNutLogin{
client: httpclient.NewCpdailyClient(),
}
}
func reverseStringASCII(s string) string {
bytes := []byte(s)
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
+10 -45
View File
@@ -3,70 +3,35 @@ package login
import (
"context"
"io"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/cst"
"likedream-goframe/internal/application/captcha"
"likedream-goframe/internal/utils"
"github.com/gogf/gf/v2/frame/g"
"github.com/samber/oops"
"github.com/tidwall/gjson"
"resty.dev/v3"
)
func ResolveOcrCaptcha(ctx context.Context, url string, client *resty.Client) string {
captchaResponse, err := client.R().
byteData := DownCode2Byte(ctx, url, client)
return captcha.ApiAutoOCRVerification(byteData)
}
func DownCode2Byte(ctx context.Context, url string, client *resty.Client) []byte {
res, err := client.R().
WithContext(ctx).
Get(url)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("captchaUrl", url).
WithContext(ctx).
Wrap(err)
})
defer func() {
_ = captchaResponse.Body.Close()
}()
ocrUrl := g.Cfg().MustGetWithEnv(ctx, cst.ConfigOcrServerUrl).String()
ocrResponse, err := httpclient.Common.R().
SetMultipartFields(&resty.MultipartField{
Name: "file",
FileName: "captcha.jpg",
ContentType: captchaResponse.Header().Get("Content-Type"),
Reader: captchaResponse.Body,
}).
WithContext(ctx).
Post(ocrUrl + "/ocr")
defer res.Body.Close()
all, err := io.ReadAll(res.Body)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("captchaUrl", url).
WithContext(ctx).
Wrap(err)
})
defer func() {
_ = ocrResponse.Body.Close()
}()
responseBytes, err := io.ReadAll(ocrResponse.Body)
utils.OkOrPanic(err)
responseJSON := gjson.ParseBytes(responseBytes)
data := responseJSON.Get("data").String()
if data == "" {
utils.OkOrPanic(oops.
In("ResolveOcrCaptcha").
With("captchaUrl", url).
With("message", responseJSON.Get("message").String()).
WithContext(ctx).
New("OCR服务返回数据为空"))
}
return data
return all
}
@@ -0,0 +1,90 @@
package login
import (
"context"
"io"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/cst"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"github.com/bytedance/sonic"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/samber/oops"
"github.com/tidwall/gjson"
"resty.dev/v3"
)
type CasSliderCaptchaReponse struct {
BigImage string `json:"bigImage"`
SmallImage string `json:"smallImage"`
TagWidth int `json:"tagWidth"`
YHeight int `json:"yHeight"`
}
type CasSliderResult struct {
ErrorCode int `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
func ResolveCasSliderCaptcha(ctx context.Context, captchaUrl string, verifyUrl string, client *resty.Client) {
res, err := client.R().
WithContext(ctx).
Get(captchaUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("captchaUrl", captchaUrl).
WithContext(ctx).
Wrap(err)
})
defer res.Body.Close()
bytes, _ := io.ReadAll(res.Body)
var captcha CasSliderCaptchaReponse
sonic.Unmarshal(bytes, &captcha)
//width := slider.VerifySliderWidthByBase64(captcha.BigImage, captcha.SmallImage)
width := ApiVerifySliderWidthByBase64(captcha.BigImage, captcha.SmallImage)
res2, err2 := client.R().
WithContext(ctx).
SetFormData(map[string]string{
"canvasLength": "280",
"moveLength": width,
}).
Post(verifyUrl)
utils.OkOrPanic(err2, func(err error) error {
return oops.
With("verifyUrl", verifyUrl).
WithContext(ctx).
Wrap(err)
})
defer res2.Body.Close()
bytes, _ = io.ReadAll(res2.Body)
str := string(bytes)
if len(str) > 0 {
var result CasSliderResult
sonic.Unmarshal(bytes, &result)
if result.ErrorCode != 1 {
utils.OkOrPanic(oops.WithContext(ctx).Wrap(errors.NewBusinessError(500, "滑块解析错误")))
}
}
}
func ApiVerifySliderWidthByBase64(bigImage string, smallImage string) string {
ctx := gctx.GetInitCtx()
baseUrl := g.Config().MustGetWithEnv(ctx, cst.ConfigCaptchaBaseUrl).String() + "/slider"
client := httpclient.Cpdaily.Clone(ctx)
client.SetDoNotParseResponse(false)
post, err := client.R().SetBody(map[string]any{
"big_image": bigImage,
"small_image": smallImage,
}).Post(baseUrl)
utils.OkOrPanic(err, func(err error) error {
return oops.
With("AutoOCRVerification", baseUrl).
WithContext(ctx).
Wrap(err)
})
result := post.String()
return gjson.Get(result, "data").String()
}
@@ -0,0 +1,78 @@
package map_info
import (
"context"
"fmt"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"regexp"
"time"
"github.com/bytedance/sonic"
"github.com/samber/oops"
)
func SearchAddressLongitudeAndLatitude(ctx context.Context, address string) []MapPoi {
client := httpclient.Cpdaily.Clone(ctx).SetDoNotParseResponse(false)
// 首先访问获取ak
response, err := client.R().Get("https://lbsyun.baidu.com/jsdemo/demo/webgl0_5.htm")
utils.OkOrPanic(err, func(err error) error {
return oops.WithContext(ctx).With("url", "https://lbsyun.baidu.com/jsdemo/demo/webgl0_5.htm").In("SearchAddressLongitudeAndLatitude.GetAk").Wrap(err)
})
ak := ParseAkString(response.String())
timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
mapResponse, err := client.R().
SetQueryParams(map[string]string{
"qt": "placesug",
"query": address,
"callback": "BMapGL._rd._cbk25241",
"region": "131",
"city_limit": "false",
"t": timestamp,
"from": "jsapi",
"ak": ak,
"v": "gl",
"timeStamp": timestamp,
}).
SetHeaders(map[string]string{
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Referer": "https://lbsyun.baidu.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
}).
Get("https://api.map.baidu.com/")
utils.OkOrPanic(err, func(err error) error {
return oops.WithContext(ctx).With("address", address).In("SearchAddressLongitudeAndLatitude.QueryMap").Wrap(err)
})
// 从JSONP响应中提取JSON数据
str := ParseJsonpToJson(mapResponse.String())
var jsonData MapJSONData
sonic.Unmarshal([]byte(str), &jsonData)
return jsonData.Result.PoiList
}
// 正则提取ak // <script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=E4805d16520de693a3fe707cdc962045"></script>
func ParseAkString(data string) string {
reg := regexp.MustCompile(`ak=([^"]+)`)
matches := reg.FindStringSubmatch(data)
if len(matches) < 2 {
utils.OkOrPanic(errors.NewBusinessError(500, "获取ak出错"))
}
return matches[1]
}
// 从JSONP响应中提取JSON数据
// 输入: /**/BMapGL._rd._cbk25241 && BMapGL._rd._cbk25241({...})
// 输出: {...}
func ParseJsonpToJson(jsonpData string) string {
// 匹配 callback(json) 格式,提取括号中的内容
reg := regexp.MustCompile(`\((\{.*\})\)`)
matches := reg.FindStringSubmatch(jsonpData)
if len(matches) < 2 {
utils.OkOrPanic(errors.NewBusinessError(500, "解析JSONP数据出错"))
}
return matches[1]
}
@@ -0,0 +1,14 @@
package map_info
import (
"encoding/json"
"testing"
"github.com/gogf/gf/v2/os/gctx"
)
func TestMapSearch(t *testing.T) {
res := SearchAddressLongitudeAndLatitude(gctx.New(), "河北石油职业技术大学c座")
marshal, _ := json.Marshal(res)
t.Log(string(marshal))
}
@@ -0,0 +1,29 @@
package map_info
type MapJSONData struct {
Status int `json:"status"`
Message string `json:"message"`
Result MapResult `json:"result"`
}
type MapLocation struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
type MapPoi struct {
Name string `json:"name"`
Location MapLocation `json:"location"`
UID string `json:"uid"`
Province string `json:"province"`
City string `json:"city"`
District string `json:"district"`
Business string `json:"business"`
Cityid string `json:"cityid"`
Tag string `json:"tag"`
Address string `json:"address"`
Children []interface{} `json:"children"`
Adcode string `json:"adcode"`
}
type MapResult struct {
Q string `json:"q"`
PoiList []MapPoi `json:"poi_list"`
}
@@ -1,22 +1,27 @@
package school
import "C"
import (
"context"
"encoding/base64"
"encoding/json"
cryptoutil2 "likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/httpclient"
cst2 "likedream-goframe/internal/common/cst"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/framework/redis"
"likedream-goframe/internal/utils"
"sync"
"time"
"github.com/bytedance/sonic"
"github.com/samber/oops"
"github.com/tidwall/gjson"
)
var lock = sync.RWMutex{}
func GetSchoolList(ctx context.Context) []CpdailySchoolBriefInfo {
response, err := httpclient.Cpdaily.R().
response, err := httpclient.Cpdaily.Clone(ctx).R().
WithContext(ctx).
Get("https://static.campushoy.com/apicache/tenantListSort")
@@ -52,7 +57,17 @@ func GetSchoolList(ctx context.Context) []CpdailySchoolBriefInfo {
}
func GetSchoolDetail(ctx context.Context, tenantId string) SchoolDetailInfo {
response, err := httpclient.Cpdaily.R().Clone(ctx).
lock.Lock()
defer lock.Unlock()
client := redis.GetClient()
redisResult := client.Get(ctx, cst2.RedisSchoolInfo+tenantId)
if redisResult.Err() == nil {
var ret SchoolDetailInfo
err := sonic.Unmarshal([]byte(redisResult.Val()), &ret)
utils.OkOrPanic(err)
return parseSpecialSchool(ret)
}
response, err := httpclient.Cpdaily.Clone(ctx).R().
SetHeaders(map[string]string{
"deviceType": "2",
"CpdailyClientType": "CPDAILY",
@@ -89,6 +104,14 @@ func GetSchoolDetail(ctx context.Context, tenantId string) SchoolDetailInfo {
WithContext(ctx).
Wrap(errors.NewBusinessError(503, "学校ID不存在").Decorate("empty school detail")))
}
return ret[0]
client.Set(ctx, cst2.RedisSchoolInfo+tenantId, ret[0], time.Duration(cst2.RedisSchoolInfoExpire)*time.Second)
return parseSpecialSchool(ret[0])
}
func parseSpecialSchool(s SchoolDetailInfo) SchoolDetailInfo {
// 甘肃农业大学
if s.Id == "gsau" {
s.WecloudUrl = "https://fdm.gsau.edu.cn"
}
return s
}
@@ -1,181 +1,182 @@
package submitcaptcha
import (
"context"
"io"
"likedream-goframe/internal/application/cpdaily/types"
"likedream-goframe/internal/application/recognizer/classifier"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"strings"
"github.com/bytedance/sonic"
"resty.dev/v3"
)
type AIResolver struct {
client *resty.Client
host string
deviceId string
taskType types.TaskType
}
func NewAIResolver(client *resty.Client, host, deviceId string, taskType types.TaskType) SubmitCaptchaResolver {
return &AIResolver{
client: client,
host: host,
deviceId: deviceId,
taskType: taskType,
}
}
func (r *AIResolver) GetValidationInfo(ctx context.Context) ValidationInfo {
// GetValidationInfo 获取验证信息
reqData := map[string]string{
"deviceId": r.deviceId,
}
resp, err := r.client.R().
SetBody(reqData).
Post(r.getValidateUrl())
utils.OkOrPanic(err)
apiResp := utils.ReadJson[ApiResponse](resp.Body)
if apiResp.Datas == nil {
message := "获取提交验证码信息失败"
if apiResp.Message != "" {
message = apiResp.Message
}
utils.OkOrPanic(errors.NewBusinessError(503, message))
}
var validationInfo ValidationInfo
err = sonic.Unmarshal(apiResp.Datas, &validationInfo)
utils.OkOrPanic(err)
return validationInfo
}
func (r *AIResolver) GetCaptchaTicket(ctx context.Context, info ValidationInfo) string {
captchaInfo := r.createCaptcha(info)
codes := r.getResult(captchaInfo)
return r.submitCaptcha(info, captchaInfo, codes)
}
func (r *AIResolver) getResult(data CpSubmitCaptchaForAI) []string {
name := data.Name
infos := data.ImageInfos
var result []string
for _, item := range infos {
url := item.Path
get, err := r.client.R().Get(url)
utils.OkOrPanic(err)
defer get.Body.Close()
bytes, _ := io.ReadAll(get.Body)
//todo 将图片保存方便后面训练
//os.WriteFile(item.Code+".png", bytes, 0666)
if classifier.ClassifyImage(bytes, name) {
result = append(result, item.Code)
}
}
return result
}
// getValidateUrl 根据任务类型获取验证URL
func (r *AIResolver) getValidateUrl() string {
baseUrl := strings.TrimSuffix(r.host, "/")
switch r.taskType {
case types.TaskTypeCollection:
return baseUrl + "/wec-counselor-collector-apps/stu/collector/checkValidation"
case types.TaskTypeSign:
return baseUrl + "/wec-counselor-sign-apps/stu/sign/checkValidation"
case types.TaskTypeSleepCheck:
return baseUrl + "/wec-counselor-attendance-apps/student/attendance/checkValidation"
default:
utils.OkOrPanic(errors.NewBusinessError(400, "不支持的任务类型"))
return ""
}
}
// createCaptcha 创建验证码
func (r *AIResolver) createCaptcha(info ValidationInfo) CpSubmitCaptchaForAI {
baseUrl := strings.TrimSuffix(r.host, "/")
url := baseUrl + "/captcha-open-api/v1/captcha/create/scenesImage"
resp, err := r.client.R().
SetHeader("deviceId", r.deviceId).
SetMultipartFormData(map[string]string{
"accountKey": info.AccountKey,
"sceneCode": info.SceneCode,
"tenantId": info.TenantId,
"userId": info.UserId,
}).
Post(url)
utils.OkOrPanic(err)
apiResp := utils.ReadJson[ApiResponse](resp.Body)
if apiResp.Result == nil {
message := "获取提交验证码失败"
if apiResp.Message != "" {
message = apiResp.Message
}
utils.OkOrPanic(errors.NewBusinessError(503, message))
}
var result CpSubmitCaptchaForAI
err = sonic.Unmarshal(apiResp.Result, &result)
utils.OkOrPanic(err)
return result
}
// submitCaptcha 提交验证码
func (r *AIResolver) submitCaptcha(info ValidationInfo, submitCaptcha CpSubmitCaptchaForAI, codes []string) string {
baseUrl := strings.TrimSuffix(r.host, "/")
url := baseUrl + "/captcha-open-api/v1/captcha/validate/scenesImage"
formData := map[string]string{
"accountKey": info.AccountKey,
"sceneCode": info.SceneCode,
"tenantId": info.TenantId,
"userId": info.UserId,
"scenesImageCode": submitCaptcha.Code,
}
// 创建multipart form request
req := r.client.R().SetHeader("deviceId", r.deviceId)
// 添加基本表单数据
for key, value := range formData {
req = req.SetMultipartFormData(map[string]string{key: value})
}
// 添加验证码列表
for _, code := range codes {
req = req.SetMultipartField("scenesImageCodes", "", "", strings.NewReader(code))
}
resp, err := req.Post(url)
utils.OkOrPanic(err)
apiResp := utils.ReadJson[ApiResponse](resp.Body)
if apiResp.Code != "200" {
message := "提交验证码失败"
if apiResp.Message != "" {
message = apiResp.Message
}
utils.OkOrPanic(errors.NewBusinessError(503, message))
}
if apiResp.Result == nil {
utils.OkOrPanic(errors.NewBusinessError(503, "今日校园返回空验证码结果"))
}
var result string
err = sonic.Unmarshal(apiResp.Result, &result)
utils.OkOrPanic(err)
return result
}
//
//import (
// "context"
// "io"
// "likedream-goframe/internal/application/cpdaily/types"
// "likedream-goframe/internal/application/recognizer/classifier"
// "likedream-goframe/internal/common/errors"
// "likedream-goframe/internal/utils"
// "strings"
//
// "github.com/bytedance/sonic"
// "resty.dev/v3"
//)
//
//type AIResolver struct {
// client *resty.Client
// host string
// deviceId string
// taskType types.TaskType
//}
//
//func NewAIResolver(client *resty.Client, host, deviceId string, taskType types.TaskType) SubmitCaptchaResolver {
// return &AIResolver{
// client: client,
// host: host,
// deviceId: deviceId,
// taskType: taskType,
// }
//}
//func (r *AIResolver) GetValidationInfo(ctx context.Context) ValidationInfo {
// // GetValidationInfo 获取验证信息
// reqData := map[string]string{
// "deviceId": r.deviceId,
// }
//
// resp, err := r.client.R().
// SetBody(reqData).
// Post(r.getValidateUrl())
// utils.OkOrPanic(err)
//
// apiResp := utils.ReadJson[ApiResponse](resp.Body)
//
// if apiResp.Datas == nil {
// message := "获取提交验证码信息失败"
// if apiResp.Message != "" {
// message = apiResp.Message
// }
// utils.OkOrPanic(errors.NewBusinessError(503, message))
// }
//
// var validationInfo ValidationInfo
// err = sonic.Unmarshal(apiResp.Datas, &validationInfo)
// utils.OkOrPanic(err)
//
// return validationInfo
//
//}
//func (r *AIResolver) GetCaptchaTicket(ctx context.Context, info ValidationInfo) string {
// captchaInfo := r.createCaptcha(info)
// codes := r.getResult(captchaInfo)
// return r.submitCaptcha(info, captchaInfo, codes)
//}
//
//func (r *AIResolver) getResult(data CpSubmitCaptchaForAI) []string {
// name := data.Name
// infos := data.ImageInfos
// var result []string
// for _, item := range infos {
// url := item.Path
// get, err := r.client.R().Get(url)
// utils.OkOrPanic(err)
// defer get.Body.Close()
// bytes, _ := io.ReadAll(get.Body)
// //todo 将图片保存方便后面训练
// //os.WriteFile(item.Code+".png", bytes, 0666)
// if classifier.ClassifyImage(bytes, name) {
// result = append(result, item.Code)
// }
// }
// return result
//}
//
//// getValidateUrl 根据任务类型获取验证URL
//func (r *AIResolver) getValidateUrl() string {
// baseUrl := strings.TrimSuffix(r.host, "/")
//
// switch r.taskType {
// case types.TaskTypeCollection:
// return baseUrl + "/wec-counselor-collector-apps/stu/collector/checkValidation"
// case types.TaskTypeSign:
// return baseUrl + "/wec-counselor-sign-apps/stu/sign/checkValidation"
// case types.TaskTypeSleepCheck:
// return baseUrl + "/wec-counselor-attendance-apps/student/attendance/checkValidation"
// default:
// utils.OkOrPanic(errors.NewBusinessError(400, "不支持的任务类型"))
// return ""
// }
//}
//
//// createCaptcha 创建验证码
//func (r *AIResolver) createCaptcha(info ValidationInfo) CpSubmitCaptchaForAI {
// baseUrl := strings.TrimSuffix(r.host, "/")
// url := baseUrl + "/captcha-open-api/v1/captcha/create/scenesImage"
//
// resp, err := r.client.R().
// SetHeader("deviceId", r.deviceId).
// SetMultipartFormData(map[string]string{
// "accountKey": info.AccountKey,
// "sceneCode": info.SceneCode,
// "tenantId": info.TenantId,
// "userId": info.UserId,
// }).
// Post(url)
// utils.OkOrPanic(err)
//
// apiResp := utils.ReadJson[ApiResponse](resp.Body)
//
// if apiResp.Result == nil {
// message := "获取提交验证码失败"
// if apiResp.Message != "" {
// message = apiResp.Message
// }
// utils.OkOrPanic(errors.NewBusinessError(503, message))
// }
//
// var result CpSubmitCaptchaForAI
// err = sonic.Unmarshal(apiResp.Result, &result)
// utils.OkOrPanic(err)
// return result
//}
//
//// submitCaptcha 提交验证码
//func (r *AIResolver) submitCaptcha(info ValidationInfo, submitCaptcha CpSubmitCaptchaForAI, codes []string) string {
// baseUrl := strings.TrimSuffix(r.host, "/")
// url := baseUrl + "/captcha-open-api/v1/captcha/validate/scenesImage"
//
// formData := map[string]string{
// "accountKey": info.AccountKey,
// "sceneCode": info.SceneCode,
// "tenantId": info.TenantId,
// "userId": info.UserId,
// "scenesImageCode": submitCaptcha.Code,
// }
//
// // 创建multipart form request
// req := r.client.R().SetHeader("deviceId", r.deviceId)
//
// // 添加基本表单数据
// for key, value := range formData {
// req = req.SetMultipartFormData(map[string]string{key: value})
// }
//
// // 添加验证码列表
// for _, code := range codes {
// req = req.SetMultipartField("scenesImageCodes", "", "", strings.NewReader(code))
// }
//
// resp, err := req.Post(url)
// utils.OkOrPanic(err)
//
// apiResp := utils.ReadJson[ApiResponse](resp.Body)
//
// if apiResp.Code != "200" {
// message := "提交验证码失败"
// if apiResp.Message != "" {
// message = apiResp.Message
// }
// utils.OkOrPanic(errors.NewBusinessError(503, message))
// }
//
// if apiResp.Result == nil {
// utils.OkOrPanic(errors.NewBusinessError(503, "今日校园返回空验证码结果"))
// }
//
// var result string
// err = sonic.Unmarshal(apiResp.Result, &result)
// utils.OkOrPanic(err)
//
// return result
//}
@@ -42,9 +42,7 @@ type ApiResponse struct {
type ITStarResponse struct {
Code json.Number `json:"code"`
Msg string `json:"msg"`
Data struct {
SuccCode []string `json:"succCode"`
} `json:"data"`
Data []string `json:"data"`
}
type SubmitCaptchaResolver interface {
@@ -16,7 +16,7 @@ import (
"resty.dev/v3"
)
type RuoliSubmitCaptchaResolver struct {
type NormalSubmitCaptchaResolver struct {
client *resty.Client
host string
deviceId string
@@ -24,12 +24,12 @@ type RuoliSubmitCaptchaResolver struct {
itstarUrl string
}
var _ SubmitCaptchaResolver = (*RuoliSubmitCaptchaResolver)(nil)
var _ SubmitCaptchaResolver = (*NormalSubmitCaptchaResolver)(nil)
// NewRuoliResolver 创建若李验证码解析器
func NewRuoliResolver(client *resty.Client, host, deviceId string, taskType types.TaskType) *RuoliSubmitCaptchaResolver {
baseUrl := g.Config().MustGetWithEnv(gctx.GetInitCtx(), cst.ConfigRuoliCaptchaBaseUrl).String()
return &RuoliSubmitCaptchaResolver{
// NewNormalResolver 创建若李验证码解析器
func NewNormalResolver(client *resty.Client, host, deviceId string, taskType types.TaskType) *NormalSubmitCaptchaResolver {
baseUrl := g.Config().MustGetWithEnv(gctx.GetInitCtx(), cst.ConfigCaptchaBaseUrl).String() + "/classifier"
return &NormalSubmitCaptchaResolver{
client: client,
host: host,
deviceId: deviceId,
@@ -39,12 +39,12 @@ func NewRuoliResolver(client *resty.Client, host, deviceId string, taskType type
}
// getITStarUrl 获取ITStar服务URL
func (r *RuoliSubmitCaptchaResolver) getITStarUrl() string {
func (r *NormalSubmitCaptchaResolver) getITStarUrl() string {
if r.itstarUrl != "" {
return r.itstarUrl
}
r.itstarUrl = g.Config().MustGetWithEnv(gctx.GetInitCtx(), cst.ConfigRuoliCaptchaBaseUrl).String()
r.itstarUrl = g.Config().MustGetWithEnv(gctx.GetInitCtx(), cst.ConfigCaptchaBaseUrl).String() + "/classifier"
if r.itstarUrl == "" {
utils.OkOrPanic(errors.NewBusinessError(500, "未配置ITStarUrl"))
}
@@ -53,7 +53,7 @@ func (r *RuoliSubmitCaptchaResolver) getITStarUrl() string {
}
// getValidateUrl 根据任务类型获取验证URL
func (r *RuoliSubmitCaptchaResolver) getValidateUrl() string {
func (r *NormalSubmitCaptchaResolver) getValidateUrl() string {
baseUrl := strings.TrimSuffix(r.host, "/")
switch r.taskType {
@@ -70,7 +70,7 @@ func (r *RuoliSubmitCaptchaResolver) getValidateUrl() string {
}
// GetValidationInfo 获取验证信息
func (r *RuoliSubmitCaptchaResolver) GetValidationInfo(ctx context.Context) ValidationInfo {
func (r *NormalSubmitCaptchaResolver) GetValidationInfo(ctx context.Context) ValidationInfo {
reqData := map[string]string{
"deviceId": r.deviceId,
}
@@ -98,14 +98,14 @@ func (r *RuoliSubmitCaptchaResolver) GetValidationInfo(ctx context.Context) Vali
}
// GetCaptchaTicket 获取验证码票据
func (r *RuoliSubmitCaptchaResolver) GetCaptchaTicket(ctx context.Context, info ValidationInfo) string {
func (r *NormalSubmitCaptchaResolver) GetCaptchaTicket(ctx context.Context, info ValidationInfo) string {
captchaInfo := r.createCaptcha(info)
codes := r.getResultFromItStar(captchaInfo.RawResponseJson)
return r.submitCaptcha(info, captchaInfo, codes)
}
// createCaptcha 创建验证码
func (r *RuoliSubmitCaptchaResolver) createCaptcha(info ValidationInfo) CpSubmitCaptcha {
func (r *NormalSubmitCaptchaResolver) createCaptcha(info ValidationInfo) CpSubmitCaptcha {
baseUrl := strings.TrimSuffix(r.host, "/")
url := baseUrl + "/captcha-open-api/v1/captcha/create/scenesImage"
@@ -139,16 +139,13 @@ func (r *RuoliSubmitCaptchaResolver) createCaptcha(info ValidationInfo) CpSubmit
}
// getResultFromItStar 从ITStar获取识别结果
func (r *RuoliSubmitCaptchaResolver) getResultFromItStar(rawResponse json.RawMessage) []string {
func (r *NormalSubmitCaptchaResolver) getResultFromItStar(rawResponse json.RawMessage) []string {
// 解析原始响应为map以便添加authKey
var responseMap map[string]any
err := sonic.Unmarshal(rawResponse, &responseMap)
utils.OkOrPanic(err)
responseMap["authKey"] = "ITStarYYDS"
itstarUrl := strings.TrimSuffix(r.getITStarUrl(), "/")
url := itstarUrl + "/captcha/unLimitValidate"
url := r.getITStarUrl()
resp, err := r.client.R().
SetBody(responseMap).
@@ -161,11 +158,11 @@ func (r *RuoliSubmitCaptchaResolver) getResultFromItStar(rawResponse json.RawMes
utils.OkOrPanic(errors.NewBusinessError(503, fmt.Sprintf("验证码识别出错: %s", itstarResp.Msg)))
}
return itstarResp.Data.SuccCode
return itstarResp.Data
}
// submitCaptcha 提交验证码
func (r *RuoliSubmitCaptchaResolver) submitCaptcha(info ValidationInfo, submitCaptcha CpSubmitCaptcha, codes []string) string {
func (r *NormalSubmitCaptchaResolver) submitCaptcha(info ValidationInfo, submitCaptcha CpSubmitCaptcha, codes []string) string {
baseUrl := strings.TrimSuffix(r.host, "/")
url := baseUrl + "/captcha-open-api/v1/captcha/validate/scenesImage"
@@ -10,7 +10,7 @@ import (
func TestRuoliSubmitCaptchaResolver_GetCaptchaTicket(t *testing.T) {
loginClient, detail := testutil.LoginTestUser()
resolver := NewRuoliResolver(loginClient, detail.WecloudUrl, "abcdefg", types.TaskTypeSleepCheck)
resolver := NewNormalResolver(loginClient, detail.WecloudUrl, "abcdefg", types.TaskTypeSleepCheck)
info := resolver.GetValidationInfo(context.Background())
+6 -11
View File
@@ -2,27 +2,22 @@ package task
import (
"context"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
errors2 "likedream-goframe/internal/common/errors"
"likedream-goframe/internal/ent/cptask"
"likedream-goframe/internal/utils"
"resty.dev/v3"
)
func GetCpdailyTaskService(ctx context.Context, Type cptask.TaskType, cookie string, schoolId string) CpdailyTask {
detail := school.GetSchoolDetail(ctx, schoolId)
cookieJar := cookiejar.NewPersistCookieJar()
cookieJar.Deserialize(cookie)
client := httpclient.Cpdaily.Clone(ctx)
client.SetCookieJar(cookieJar)
func GetCpdailyTaskService(ctx context.Context, Type cptask.TaskType, client *resty.Client, schoolDetail school.SchoolDetailInfo) CpdailyTask {
switch Type {
case cptask.TaskTypeCollection:
return NewCollectionTask(client, detail.WecloudUrl)
return NewCollectionTask(client, schoolDetail.WecloudUrl)
case cptask.TaskTypeSign:
return NewAutoSignTask(client, detail.WecloudUrl)
return NewAutoSignTask(client, schoolDetail.WecloudUrl)
case cptask.TaskTypeSleepCheck:
return NewSleepCheckTask(client, detail.WecloudUrl)
return NewSleepCheckTask(client, schoolDetail.WecloudUrl)
default:
utils.OkOrPanic(errors2.NewBusinessError(400, "不支持的任务类型"))
}
@@ -354,7 +354,7 @@ func (c *CpdailyCollectionTask) ParseFormJson(ctx context.Context, rawJson map[s
return list
}
func (c *CpdailyCollectionTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, username string) map[string]*gvar.Var {
func (c *CpdailyCollectionTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, imgId string) map[string]*gvar.Var {
taskInfo := form["taskInfo"].MapStrVar()
rows := form["rows"].Array()
filledForm := make([]map[string]*gvar.Var, 0)
@@ -15,4 +15,5 @@ type CpTaskFormItem struct {
Sort uint `json:"sort,omitempty"`
CacheKey string `json:"cacheKey,omitempty"`
Show bool `json:"show,omitempty"`
Tip string `json:"tip,omitempty"`
}
@@ -8,4 +8,5 @@ const (
ItemTypeMulti
ItemTypeImage
ItemTypeAddress
ItemTypeLatAndLon
)
@@ -12,6 +12,6 @@ type CpdailyTask interface {
QueryFormByTitle(ctx context.Context, titleGlob string) map[string]*gvar.Var
QueryFormByYearMonth(ctx context.Context, titleGlob string, year, month string) map[string]*gvar.Var
ParseFormJson(ctx context.Context, rawJson map[string]*gvar.Var) []*formtype.CpTaskFormItem
FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, username string) map[string]*gvar.Var
FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, imgId string) map[string]*gvar.Var
Submit(ctx context.Context, form map[string]*gvar.Var, username, deviceId string)
}
+36 -35
View File
@@ -12,7 +12,6 @@ import (
"likedream-goframe/internal/application/cpdaily/types"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"math/rand/v2"
"slices"
"strings"
"time"
@@ -360,19 +359,27 @@ func (c *CpdailyAutoSignTask) ParseFormJson(ctx context.Context, rawJson map[str
var list []*formtype.CpTaskFormItem
// 经纬度
//dto := &formtype.CpTaskFormItem{
// Title: "经度",
// ColName: "lon",
// Type: formtype.ItemTypeInput,
// Required: true,
//}
//list = append(list, dto)
//
//dto = &formtype.CpTaskFormItem{
// Title: "纬度",
// ColName: "lat",
// Type: formtype.ItemTypeInput,
// Required: true,
//}
//list = append(list, dto)
dto := &formtype.CpTaskFormItem{
Title: "经度",
ColName: "lon",
Type: formtype.ItemTypeInput,
Required: true,
}
list = append(list, dto)
dto = &formtype.CpTaskFormItem{
Title: "纬度",
ColName: "lat",
Type: formtype.ItemTypeInput,
Title: "经纬度",
ColName: "latAndLon",
Type: formtype.ItemTypeLatAndLon,
Required: true,
Tip: "您可以通过地址搜索签到经纬度,如果觉得不够准确可以自行修改",
}
list = append(list, dto)
@@ -381,6 +388,7 @@ func (c *CpdailyAutoSignTask) ParseFormJson(ctx context.Context, rawJson map[str
ColName: "address",
Type: formtype.ItemTypeInput,
Required: true,
Tip: "这个就是指签到完毕后查看时最底部那个最详细的地址信息哦",
}
list = append(list, dto)
@@ -390,6 +398,7 @@ func (c *CpdailyAutoSignTask) ParseFormJson(ctx context.Context, rawJson map[str
ColName: "signImg",
Type: formtype.ItemTypeImage,
Required: true,
Tip: "您可以上传多张图片,会顺序轮换更新的哦~",
}
list = append(list, dto)
}
@@ -411,6 +420,7 @@ func (c *CpdailyAutoSignTask) ParseFormJson(ctx context.Context, rawJson map[str
IsOtherItems: false,
},
},
Tip: "一般在学校就是选否,如果在外实习、请假之类的才选是",
}
list = append(list, dto)
@@ -467,7 +477,7 @@ func (c *CpdailyAutoSignTask) ParseFormJson(ctx context.Context, rawJson map[str
return list
}
func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, username string) map[string]*gvar.Var {
func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, imgId string) map[string]*gvar.Var {
var resultForm map[string]*gvar.Var
// 填充必要变量
@@ -485,6 +495,11 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
lon = v.Content
case "address":
address = v.Content
case "latAndLon":
{
res := strings.Split(v.Content, ",")
lat, lon = res[1], res[0]
}
}
}
@@ -508,27 +523,11 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
utils.OkOrPanic(errors.NewBusinessError(400, "签到需要图片,但是当前图片为空"), oops.WithContext(ctx).Wrap)
}
imageUuidSeq := strings.SplitSeq(targetParam.Content, ",")
var imageUuids []string
for imageUuid := range imageUuidSeq {
imageUuid = strings.TrimSpace(imageUuid)
if imageUuid == "" {
continue
}
imageUuids = append(imageUuids, imageUuid)
}
if len(imageUuids) == 0 {
if imgId == "" {
utils.OkOrPanic(errors.NewBusinessError(400, "签到照片未上传"), oops.WithContext(ctx).Wrap)
}
// 随机选择一张照片
imageUuid := imageUuids[rand.IntN(len(imageUuids))]
imageId, e := uuid.Parse(imageUuid)
imageId, e := uuid.Parse(imgId)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
previewUrl := imageuploader.UploadImageFromStorage(ctx, c.host, imageId, types.TaskTypeSign, c.client)
@@ -537,7 +536,7 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
}
// 处理验证码
submitCaptchaResolver := submitcaptcha.NewRuoliResolver(c.client, c.host, deviceId, types.TaskTypeSign)
submitCaptchaResolver := submitcaptcha.NewNormalResolver(c.client, c.host, deviceId, types.TaskTypeSign)
captchaInfo := submitCaptchaResolver.GetValidationInfo(ctx)
if captchaInfo.Validation {
@@ -548,7 +547,9 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
ticket = submitCaptchaResolver.GetCaptchaTicket(ctx, captchaInfo)
})
}, retry.MaxDelay(15*time.Second), retry.Attempts(5), retry.Context(ctx))
utils.OkOrPanic(errors.NewBusinessError(503, "提交验证码识别失败").WithInnerError(e), oops.WithContext(ctx).Wrap)
if e != nil {
utils.OkOrPanic(errors.NewBusinessError(503, "提交验证码识别失败").WithInnerError(e), oops.WithContext(ctx).Wrap)
}
resultForm["ticket"] = gvar.New(ticket)
} else {
@@ -591,7 +592,7 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
continue
}
fieldObj := field.(map[string]*gvar.Var)
fieldObj := gvar.New(field).MapStrVar()
fieldTitle := fieldObj["title"].String()
if targetParam.Title != fieldTitle {
@@ -602,7 +603,7 @@ func (c *CpdailyAutoSignTask) FillCustomForm(ctx context.Context, form map[strin
var extraFieldItemValue map[string]*gvar.Var
for _, item := range extraFieldItems {
itemObj := item.(map[string]*gvar.Var)
itemObj := gvar.New(item).MapStrVar()
content := itemObj["content"].String()
if content == targetParam.Content {
+40 -44
View File
@@ -13,7 +13,6 @@ import (
"likedream-goframe/internal/common/cst"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"math/rand/v2"
"strconv"
"strings"
"time"
@@ -190,11 +189,7 @@ func (c *CpdailySleepCheckTask) QueryFormByTitle(ctx context.Context, titleGlob
SetBody(map[string]any{}).
WithContext(ctx).
Post(c.getReqUrl("getStuAttendacesInOneDay"))
//
response, err = c.client.R().
SetBody(map[string]any{}).
WithContext(ctx).
Post(c.getReqUrl("getStuAttendacesInOneDay"))
utils.OkOrPanic(err, func(err error) error {
return oops.With("host", c.host).
In("CpdailySleepCheckTask.QueryFormByTitle").
@@ -440,19 +435,27 @@ func (c *CpdailySleepCheckTask) ParseFormJson(ctx context.Context, rawJson map[s
var list []*formtype.CpTaskFormItem
// 经纬度
//dto := &formtype.CpTaskFormItem{
// Title: "经度",
// ColName: "lon",
// Type: formtype.ItemTypeInput,
// Required: true,
//}
//list = append(list, dto)
//
//dto = &formtype.CpTaskFormItem{
// Title: "纬度",
// ColName: "lat",
// Type: formtype.ItemTypeInput,
// Required: true,
//}
//list = append(list, dto)
dto := &formtype.CpTaskFormItem{
Title: "经度",
ColName: "lon",
Type: formtype.ItemTypeInput,
Required: true,
}
list = append(list, dto)
dto = &formtype.CpTaskFormItem{
Title: "纬度",
ColName: "lat",
Type: formtype.ItemTypeInput,
Title: "经纬度",
ColName: "latAndLon",
Type: formtype.ItemTypeLatAndLon,
Required: true,
Tip: "您可以通过地址搜索签到经纬度,如果觉得不够准确可以自行修改",
}
list = append(list, dto)
dto = &formtype.CpTaskFormItem{
@@ -460,6 +463,7 @@ func (c *CpdailySleepCheckTask) ParseFormJson(ctx context.Context, rawJson map[s
ColName: "address",
Type: formtype.ItemTypeInput,
Required: true,
Tip: "这个就是指签到完毕后查看时最底部那个最详细的地址信息哦",
}
list = append(list, dto)
@@ -469,6 +473,7 @@ func (c *CpdailySleepCheckTask) ParseFormJson(ctx context.Context, rawJson map[s
ColName: "signImg",
Type: formtype.ItemTypeImage,
Required: true,
Tip: "您可以上传多张图片,会顺序轮换更新的哦~",
}
list = append(list, dto)
@@ -491,6 +496,7 @@ func (c *CpdailySleepCheckTask) ParseFormJson(ctx context.Context, rawJson map[s
IsOtherItems: false,
},
},
Tip: "一般在学校就是选否,如果在外实习、请假之类的才选是",
}
list = append(list, dto)
@@ -501,7 +507,7 @@ func (c *CpdailySleepCheckTask) ParseFormJson(ctx context.Context, rawJson map[s
return list
}
func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, username string) map[string]*gvar.Var {
func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[string]*gvar.Var, paramList []*formtype.CpTaskFormItem, deviceId, imgId string) map[string]*gvar.Var {
var resultForm map[string]*gvar.Var
// 填充必要变量
@@ -519,6 +525,12 @@ func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[str
lon = v.Content
case "address":
address = v.Content
case "latAndLon":
{
res := strings.Split(v.Content, ",")
lat, lon = res[1], res[0]
}
}
}
@@ -528,8 +540,8 @@ func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[str
resultForm["uaIsCpadaily"] = gvar.New(true)
// 处理验证码
//submitCaptchaResolver := submitcaptcha.NewRuoliResolver(c.client, c.host, deviceId, types.TaskTypeSleepCheck)
submitCaptchaResolver := submitcaptcha.NewAIResolver(c.client, c.host, deviceId, types.TaskTypeSleepCheck)
submitCaptchaResolver := submitcaptcha.NewNormalResolver(c.client, c.host, deviceId, types.TaskTypeSleepCheck)
//submitCaptchaResolver := submitcaptcha.NewAIResolver(c.client, c.host, deviceId, types.TaskTypeSleepCheck)
captchaInfo := submitCaptchaResolver.GetValidationInfo(ctx)
if captchaInfo.Validation {
@@ -539,7 +551,7 @@ func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[str
return utils.Catch(func() {
ticket = submitCaptchaResolver.GetCaptchaTicket(ctx, captchaInfo)
})
}, retry.Delay(15*time.Second), retry.Attempts(5), retry.Context(ctx))
}, retry.Delay(10*time.Second), retry.Attempts(5), retry.Context(ctx))
if e != nil {
utils.OkOrPanic(errors.NewBusinessError(503, "提交验证码识别失败").WithInnerError(e), oops.WithContext(ctx).Wrap)
}
@@ -561,27 +573,11 @@ func (c *CpdailySleepCheckTask) FillCustomForm(ctx context.Context, form map[str
utils.OkOrPanic(errors.NewBusinessError(503, "未找到查寝照片参数,请上传照片"), oops.WithContext(ctx).Wrap)
}
imageUuidSeq := strings.SplitSeq(targetParam.Content, ",")
var imageUuids []string
for imageUuid := range imageUuidSeq {
imageUuid = strings.TrimSpace(imageUuid)
if imageUuid == "" {
continue
}
imageUuids = append(imageUuids, imageUuid)
}
if len(imageUuids) == 0 {
if imgId == "" {
utils.OkOrPanic(errors.NewBusinessError(503, "未找到查寝照片参数,请上传照片"), oops.WithContext(ctx).Wrap)
}
// 随机选择一张照片
imageUuid := imageUuids[rand.IntN(len(imageUuids))]
imageId, e := uuid.Parse(imageUuid)
imageId, e := uuid.Parse(imgId)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
previewUrl := imageuploader.UploadImageFromStorage(ctx, c.host, imageId, types.TaskTypeSleepCheck, c.client)
@@ -661,11 +657,11 @@ func (c *CpdailySleepCheckTask) Submit(ctx context.Context, form map[string]*gva
// 构造提交数据
submitData := map[string]*gvar.Var{
"lon": form["longitude"],
"version": gvar.New(cryptoutil.EncryptVersion),
"calVersion": gvar.New(cpdailyInfo.CalVersion),
"deviceId": extensionForm["deviceId"],
"userId": gvar.New(username),
"lon": form["longitude"],
"version": gvar.New(cryptoutil.EncryptVersion),
"calVersion": gvar.New(cpdailyInfo.CalVersion),
"deviceId": extensionForm["deviceId"],
//"userId": gvar.New(username),
"systemName": gvar.New("android"),
"bodyString": gvar.New(bodyString),
"lat": form["latitude"],
@@ -11,7 +11,7 @@ import (
"github.com/gogf/gf/v2/container/gvar"
)
const submitSignSalt = "HashSalt" // 请根据实际情况替换为真实盐值
const submitSignSalt = "hru89rwr87Nthifa" // 请根据实际情况替换为真实盐值
func GetSubmitSign(form map[string]*gvar.Var) string {
// 按照指定顺序构建哈希数据
+25 -2
View File
@@ -10,11 +10,12 @@ import (
var Cpdaily = resty.New().
SetCookieJar(nil).
SetTimeout(30 * time.Second).
SetTimeout(30*time.Second).
SetDoNotParseResponse(true).
SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
})
}).
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
func init() {
if !utils.IsDebug() {
@@ -23,3 +24,25 @@ func init() {
Cpdaily.SetProxy("http://127.0.0.1:9000")
}
}
// NewCpdailyClient 创建一个全新的独立 client 实例,避免 Clone 可能的浅拷贝问题
// 每个实例都是完全独立的,适用于并发场景,不会共享状态
func NewCpdailyClient() *resty.Client {
client := resty.New().
SetCookieJar(nil).
SetTimeout(30*time.Second).
SetDoNotParseResponse(true).
SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
}).
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
// 根据调试模式设置代理和调试选项
if !utils.IsDebug() {
client.SetDebug(false)
} else {
client.SetProxy("http://127.0.0.1:9000")
}
return client
}
@@ -24,3 +24,25 @@ func init() {
Xuexitong.SetProxy("http://127.0.0.1:9000")
}
}
// NewXuexitongClient 创建一个全新的独立 client 实例,避免 Clone 可能的浅拷贝问题
// 每个实例都是完全独立的,适用于并发场景,不会共享状态
func NewXuexitongClient() *resty.Client {
client := resty.New().
SetCookieJar(nil).
SetTimeout(30*time.Second).
SetDoNotParseResponse(false).
SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
}).
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
// 根据调试模式设置代理和调试选项
if !utils.IsDebug() {
client.SetDebug(false)
} else {
client.SetProxy("http://127.0.0.1:9000")
}
return client
}
@@ -1,196 +0,0 @@
package classifier
import (
"bufio"
"fmt"
_ "image/jpeg"
_ "image/png"
"os"
ort "github.com/yalue/onnxruntime_go"
)
type ImageClassifier struct {
session *ort.AdvancedSession
inputShape []int64
classes []string
inputTensor *ort.Tensor[float32]
outputTensor *ort.Tensor[float32]
}
// NewImageClassifier 创建新的图片分类器
func NewImageClassifier(modelPath, classesPath string) (*ImageClassifier, error) {
// 加载类别名称
classes, err := loadClasses(classesPath)
if err != nil {
return nil, fmt.Errorf("加载类别失败: %v", err)
}
// 创建session选项
options, err := ort.NewSessionOptions()
if err != nil {
return nil, fmt.Errorf("创建session选项失败: %v", err)
}
defer options.Destroy()
// 创建输入输出tensor
inputShape := ort.NewShape(1, 3, 224, 224)
outputShape := ort.NewShape(1, int64(len(classes)))
inputTensor, err := ort.NewEmptyTensor[float32](inputShape)
if err != nil {
return nil, fmt.Errorf("创建输入tensor失败: %v", err)
}
outputTensor, err := ort.NewEmptyTensor[float32](outputShape)
if err != nil {
inputTensor.Destroy()
return nil, fmt.Errorf("创建输出tensor失败: %v", err)
}
// 加载模型
session, err := ort.NewAdvancedSession(modelPath,
[]string{"input"}, []string{"output"},
[]ort.ArbitraryTensor{inputTensor}, []ort.ArbitraryTensor{outputTensor},
options)
if err != nil {
inputTensor.Destroy()
outputTensor.Destroy()
return nil, fmt.Errorf("加载模型失败: %v", err)
}
return &ImageClassifier{
session: session,
inputShape: []int64{1, 3, 224, 224},
classes: classes,
inputTensor: inputTensor,
outputTensor: outputTensor,
}, nil
}
// loadClasses 从文件加载类别名称
func loadClasses(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var classes []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
classes = append(classes, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
return classes, nil
}
// Predict 预测图片类别
func (ic *ImageClassifier) Predict(imgPath string) (string, float32, error) {
// 加载并预处理图片
inputData, err := ic.preprocessImage(imgPath)
if err != nil {
return "", 0, fmt.Errorf("预处理图片失败: %v", err)
}
// 设置输入数据
copy(ic.inputTensor.GetData(), inputData)
// 运行推理
err = ic.session.Run()
if err != nil {
return "", 0, fmt.Errorf("推理失败: %v", err)
}
// 获取输出数据
outputData := ic.outputTensor.GetData()
// 找到最大概率的类别
maxIdx := 0
maxProb := outputData[0]
for i, prob := range outputData {
if prob > maxProb {
maxProb = prob
maxIdx = i
}
}
// 应用softmax获取概率
expSum := float32(0)
for _, v := range outputData {
expSum += exp(v)
}
probability := exp(outputData[maxIdx]) / expSum
return ic.classes[maxIdx], probability, nil
}
// PredictFromBytes 从字节数据预测图片类别
func (ic *ImageClassifier) PredictFromBytes(imgData []byte) (string, float32, error) {
// 预处理图片
inputData, err := ic.preprocessImageBytes(imgData)
if err != nil {
return "", 0, fmt.Errorf("预处理图片失败: %v", err)
}
// 设置输入数据
copy(ic.inputTensor.GetData(), inputData)
// 运行推理
err = ic.session.Run()
if err != nil {
return "", 0, fmt.Errorf("推理失败: %v", err)
}
// 获取输出数据
outputData := ic.outputTensor.GetData()
// 找到最大概率的类别
maxIdx := 0
maxProb := outputData[0]
for i, prob := range outputData {
if prob > maxProb {
maxProb = prob
maxIdx = i
}
}
// 应用softmax获取概率
expSum := float32(0)
for _, v := range outputData {
expSum += exp(v)
}
probability := exp(outputData[maxIdx]) / expSum
return ic.classes[maxIdx], probability, nil
}
// Close 关闭分类器
func (ic *ImageClassifier) Close() error {
if ic.inputTensor != nil {
ic.inputTensor.Destroy()
}
if ic.outputTensor != nil {
ic.outputTensor.Destroy()
}
if ic.session != nil {
ic.session.Destroy()
}
return nil
}
// exp 计算e的x次方(简单实现)
func exp(x float32) float32 {
// 使用泰勒级数近似
result := float32(1.0)
term := float32(1.0)
for i := 1; i < 20; i++ {
term *= x / float32(i)
result += term
}
return result
}
@@ -1,8 +0,0 @@
package classifier
// ClassifyImage 图片数据 & 类别名称
// return 是否匹配
func ClassifyImage(imgData []byte, className string) bool {
name, _, _ := imageClassifier.PredictFromBytes(imgData)
return className == name
}
@@ -1,53 +0,0 @@
package classifier
import (
"embed"
_ "embed"
"likedream-goframe/internal/application/recognizer"
"likedream-goframe/internal/framework/logger"
"likedream-goframe/internal/utils"
"os"
"path/filepath"
"strings"
"go.uber.org/zap"
)
//go:embed static/*
var fs embed.FS
var imageClassifier *ImageClassifier
func init() {
if !recognizer.OnnxIsInit {
recognizer.InitOnnx()
}
modelPath, classesPath := initModel()
classifier, err := NewImageClassifier(modelPath, classesPath)
if err != nil {
logger.Logger.Panic("初始化ai模型失败", zap.Error(err))
}
imageClassifier = classifier
}
func initModel() (modelPath, classesPath string) {
// 检测文件是否存在
exePath := strings.ToLower(os.Args[0])
// 获取绝对路径
absPath, _ := filepath.Abs(exePath)
staticPath := filepath.Join(filepath.Dir(absPath), "static")
if _, err := os.Stat(staticPath); os.IsNotExist(err) {
os.Mkdir(staticPath, 0755)
}
modelPath = filepath.Join(staticPath, "model.onnx")
modelBytes, err := fs.ReadFile("static/model.onnx")
utils.OkOrPanic(err)
if _, err := os.Stat(modelPath); os.IsNotExist(err) {
os.WriteFile(modelPath, modelBytes, 0644)
}
classesPath = filepath.Join(staticPath, "classes.txt")
classesBytes, err := fs.ReadFile("static/classes.txt")
utils.OkOrPanic(err)
if _, err := os.Stat(classesPath); os.IsNotExist(err) {
os.WriteFile(classesPath, classesBytes, 0644)
}
return
}
@@ -1,120 +0,0 @@
package classifier
import (
"bytes"
"image"
"os"
)
// preprocessImage 预处理图片文件
func (ic *ImageClassifier) preprocessImage(imgPath string) ([]float32, error) {
file, err := os.Open(imgPath)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return ic.processImage(img)
}
// preprocessImageBytes 预处理图片字节数据
func (ic *ImageClassifier) preprocessImageBytes(imgData []byte) ([]float32, error) {
img, _, err := image.Decode(bytes.NewReader(imgData))
if err != nil {
return nil, err
}
return ic.processImage(img)
}
// processImage 处理图片为模型输入格式
func (ic *ImageClassifier) processImage(img image.Image) ([]float32, error) {
// 调整图片大小到224x224
resized := resizeImage(img, 224, 224)
// 转换为float32数组,并进行归一化
// ImageNet的均值和标准差
mean := []float32{0.485, 0.456, 0.406}
std := []float32{0.229, 0.224, 0.225}
data := make([]float32, 3*224*224)
for y := 0; y < 224; y++ {
for x := 0; x < 224; x++ {
r, g, b, _ := resized.At(x, y).RGBA()
// 转换为0-1范围
rNorm := float32(r) / 65535.0
gNorm := float32(g) / 65535.0
bNorm := float32(b) / 65535.0
// 应用归一化
idx := y*224 + x
data[idx] = (rNorm - mean[0]) / std[0] // R通道
data[224*224+idx] = (gNorm - mean[1]) / std[1] // G通道
data[2*224*224+idx] = (bNorm - mean[2]) / std[2] // B通道
}
}
return data, nil
}
// resizeImage 调整图片大小
func resizeImage(img image.Image, width, height int) image.Image {
bounds := img.Bounds()
srcW := bounds.Dx()
srcH := bounds.Dy()
dst := image.NewRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// 简单的最近邻插值
srcX := x * srcW / width
srcY := y * srcH / height
if srcX >= srcW {
srcX = srcW - 1
}
if srcY >= srcH {
srcY = srcH - 1
}
dst.Set(x, y, img.At(srcX, srcY))
}
}
return dst
}
// centerCrop 中心裁剪图片
func centerCrop(img image.Image, size int) image.Image {
bounds := img.Bounds()
width := bounds.Dx()
height := bounds.Dy()
var x0, y0 int
if width > height {
x0 = (width - height) / 2
y0 = 0
width = height
} else {
x0 = 0
y0 = (height - width) / 2
height = width
}
cropped := image.NewRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
cropped.Set(x, y, img.At(x0+x, y0+y))
}
}
return resizeImage(cropped, size, size)
}
@@ -1,23 +0,0 @@
公交车
向日葵
多肉
摩托车
樱桃
汽车
满天星
狗
狮子
猫
老虎
自行车
芒果
苹果
荷花
葡萄
西瓜
豹子
长颈鹿
青桔
飞机
香蕉
高铁
@@ -1,109 +0,0 @@
package ddddocr
import (
"fmt"
"regexp"
"strings"
"github.com/thedevsaddam/gojsonq"
ort "github.com/yalue/onnxruntime_go"
"image"
"log"
"strconv"
)
// SemiOCRVerification 半自动验证码识别(要自己传入outputShape值)
func SemiOCRVerification(img image.Image, outputShape ort.Shape) string {
img1 := ResizeImage(img, uint(64*img.Bounds().Dx()/img.Bounds().Dy()), 64)
imgGray := ConvertToGray(img1)
inputData := ImageToGrayFloatArray(imgGray)
inputShape := ort.NewShape(1, 1, 64, int64(imgGray.Bounds().Dx()))
inputTensor, err := ort.NewTensor[float32](inputShape, inputData)
if err != nil {
panic(err)
}
defer inputTensor.Destroy()
// This hypothetical network maps a 2x5 input -> 2x3x4 output.
outputTensor, err := ort.NewEmptyTensor[int64](outputShape)
defer outputTensor.Destroy()
onnxPath := initModel()
session, err := ort.NewAdvancedSession(onnxPath,
[]string{"input1"}, []string{"output"},
[]ort.Value{inputTensor}, []ort.Value{outputTensor}, nil)
defer session.Destroy()
if err != nil {
log.Fatal(err)
}
err = session.Run()
if err != nil {
fmt.Errorf(err.Error())
}
outputData := outputTensor.GetData()
codeResult := ""
for i := 0; i < len(outputData); i++ {
if outputData[i] != 0 {
codeResult += gojsonq.New().JSONString(getCharCode()).Find("[" + strconv.Itoa(int(outputData[i])) + "]").(string)
}
}
return codeResult
}
// AutoOCRVerification 全自动
func AutoOCRVerification(img image.Image) string {
img1 := ResizeImage(img, uint(64*img.Bounds().Dx()/img.Bounds().Dy()), 64)
imgGray := ConvertToGray(img1)
inputData := ImageToGrayFloatArray(imgGray)
inputShape := ort.NewShape(1, 1, 64, int64(imgGray.Bounds().Dx()))
inputTensor, err := ort.NewTensor[float32](inputShape, inputData)
if err != nil {
panic(err)
}
defer inputTensor.Destroy()
var outputShape = ort.NewShape(1, 16)
codeResult := ""
//失败一次获取输出纬度
for {
outputTensor, err := ort.NewEmptyTensor[int64](outputShape)
defer outputTensor.Destroy()
onnxPath := initModel()
session, err := ort.NewAdvancedSession(onnxPath,
[]string{"input1"}, []string{"output"},
[]ort.Value{inputTensor}, []ort.Value{outputTensor}, nil)
defer session.Destroy()
if err != nil {
log.Fatal(err)
}
err1 := session.Run()
if err1 != nil {
if strings.Contains(err1.Error(), "OrtValue shape verification failed. Current shape:") {
regex := regexp.MustCompile(`Requested shape:{(\d+),(\d+)}`)
matches := regex.FindStringSubmatch(err1.Error())
if len(matches) == 3 {
c1, _ := strconv.Atoi(matches[1])
c2, _ := strconv.Atoi(matches[2])
outputShape = ort.NewShape(int64(c1), int64(c2))
continue
}
}
fmt.Errorf(err1.Error())
return codeResult
}
outputData := outputTensor.GetData()
for i := 0; i < len(outputData); i++ {
if outputData[i] != 0 {
codeResult += gojsonq.New().JSONString(getCharCode()).Find("[" + strconv.Itoa(int(outputData[i])) + "]").(string)
}
}
break
}
return codeResult
}
@@ -1,870 +0,0 @@
package ddddocr
// 获取映射字符串
func getCharCode() string {
return `["", "掀", "袜", "顧", "徕", "榱", "荪", "浡", "其", "炎", "玉", "恩", "劣", "徽", "廉", "桂", "拂",
"鳊", "撤",
"赏", "哮", "侄", "蓮", "И", "进", "饭", "饱", "优", "楸", "礻", "蜉", "營", "伙", "杌", "修", "榜",
"准", "铒",
"戏", "赭", "襟", "彘", "彩", "雁", "闽", "坎", "聂", "氡", "辜", "苁", "潆", "摁", "月", "稇", "而",
"醴", "簉",
"卑", "妖", "埽", "嘡", "醛", "見", "煎", "汪", "秽", "迄", "噭", "焉", "钌", "瑕", "玻", "仙", "蹑",
"钀", "翦",
"丰", "矗", "2", "胚", "镊", "镡", "鍊", "帖", "僰", "淀", "吒", "冲", "挡", "粼", "螈", "缵", "孺",
"侦", "曷",
"渐", "敷", "投", "宸", "祉", "柳", "尖", "梃", "淘", "臁", "躇", "撖", "惭", "狄", "聢", "官", "狴",
"诬", "骄",
"跻", "場", "姻", "钎", "藥", "綉", "驾", "舻", "黢", "鲦", "蜣", "渖", "绹", "佰", "怜", "三", "痪",
"眍", "养",
"角", "薜", "濑", "劳", "戟", "傎", "纫", "徉", "收", "稍", "虫", "螋", "鬲", "捌", "陡", "蓟", "邳",
"蹢", "涉",
"煋", "端", "懷", "椤", "埶", "廊", "免", "秫", "猢", "睐", "臺", "擀", "布", "麃", "彗", "汊", "芄",
"遣", "胙",
"另", "癯", "徭", "疢", "茆", "忡", "'", "烃", "笕", "薤", "肆", "熛", "過", "盖", "跷", "呷", "痿",
"沖", "魍",
"讣", "庤", "弑", "诩", "庵", "履", "暮", "始", "滟", "矅", "蛹", "鸿", "啃", "铋", "沿", "鐾", "酆",
"團", "恙",
"閥", "聒", "讵", "颠", "沾", "堅", "踣", "陴", "覃", "滙", "浐", "钇", "脆", "炙", "亮", "觌", "産",
"汩", "鸭",
"斄", "堆", "掭", "揞", "鹂", "郫", "瘅", "蚂", "揩", "学", "组", "浸", "腙", "耀", "嗛", "局", "蠓",
"肠", "昏",
"I", "岑", "镯", "憧", "油", "泸", "鸟", "潇", "蕻", "褒", "瞧", "旸", "昭", "庐", "鞒", "内", "痈",
"己", "曙",
"怠", "锟", "晞", "耢", "鲢", "醦", "糕", "療", "寇", "梵", "黾", "呻", "苒", "ü", "校", "嘏", "昃",
"Ⅰ", "蕰",
"凖", "嵛", "裨", "筏", "匜", "咋", "乏", "婵", "镂", "珰", "感", "蔗", "蚵", "庞", "弢", "槟", "口",
"漉", "﹒",
"咂", "俩", "增", "硐", "襙", "绉", "卿", "距", "璱", "猖", "铚", "郚", "嬖", "缒", "阃", "扞", "V",
"望", "最",
"浔", "骜", "赃", "闻", "砍", "奸", "灶", "以", "获", "鳎", "浦", "罐", "孓", "纭", "瘀", "普", "氰",
"塮", "症",
"顷", "们", "螓", "蛸", "鵰", "册", "美", "萨", "沘", "犰", "嫌", "名", ")", "懦", "滇", "F", "垡",
"声", "毅",
"隅", "鲎", "煨", "萦", "宜", "唇", "鯨", "邛", "杲", "赜", "长", "魂", "桠", "锇", "搓", "俘", "仰",
"膘", "宦",
"歹", "遁", "猃", "噉", "幂", "糜", "嗤", "周", "剂", "曦", "暧", "焖", "髻", "釐", "泰", "窟", "檎",
"旧", "犀",
"镄", "百", "取", "岍", "逗", "叽", "呃", "鲪", "萬", "陈", "7", "習", "区", "逄", "宏", "罡", "漭",
"盗", "郿",
"般", "谢", "倪", "纵", "婶", "砧", "揖", "扪", "濒", "愤", "茓", "浞", "子", "揄", "旌", "趄", "樊",
"醑", "遄",
"婚", "汶", "矩", "裈", "弊", "呱", "铳", "勿", "蚴", "忿", "褓", "缚", "酱", "璞", "庆", "除", "礌",
"珩", "榨",
"鼢", "逞", "容", "圯", "猛", "陌", "-", "嚯", "镘", "鱾", "睚", "猬", "杜", "鳓", "燈", "計", "咣",
"炜", "睁",
"箱", "邮", "略", "馇", "逐", "雀", "僬", "髯", "奖", "俱", "-", "绗", "犏", "辱", "忑", "挽", "康",
"蝼", "栏",
"模", "辒", "•", "儋", "罱", "墈", "会", "秀", "栈", "缔", "醜", "蚣", "阮", "鼗", "眼", "湧", "沁",
"夥", "毕",
"媚", "瘳", "痣", "搴", "闿", "遍", "焰", "岣", "舱", "埌", "麿", "嘿", "靽", "体", "想", "霓", "钛",
"摽", "苑",
"芳", "技", "綮", "钅", "燠", "栾", "年", "悱", "腹", "员", "呕", "闇", "嗫", "檩", "荒", "溱", "舨",
"峙", "卒",
"洑", "预", "弯", "蔷", "叵", "锯", "慈", "牧", "患", "贇", "偷", "鲜", "锓", "躔", "嚬", "烈", "娌",
"嘲", "详",
"麺", "舒", "厨", "徵", "葹", "只", "篦", "鹀", "剕", "驳", "聍", "黧", "砾", "暅", "褫", "呈", "森",
"结", "龛",
"钲", "轧", "扔", "蕹", "赵", "涒", "冯", "渲", "缭", "坚", "趼", "鲑", "倫", "门", "班", "垚", "鞍",
"菘", "畐",
"僇", "侉", "禢", "轳", "饦", "兽", "呯", "捂", "樨", "卧", "栝", "豭", "冶", "鉰", "申", "蜈", "印",
"缨", "镫",
"蕾", "圜", "扑", "娉", "烦", "缳", "广", "峄", "獒", "铔", "奁", "醚", "倥", "蹇", "阚", "镆", "煺",
"德", "颉",
"嗅", "绷", "蒯", "祺", "崧", "往", "枨", "涡", "鲲", "瓅", "岌", "肘", "飔", "缘", "千", "棱", "溶",
"窣", "篼",
"代", "捡", "送", "咡", "术", "滑", "茜", "晾", "挤", "曳", "糈", "G", "翊", "殴", "妹", "溥", "璆",
"烩", "拙",
"襄", "几", "嘴", "D", "驮", "淙", "蹐", "合", "環", "剑", "怪", "褂", "畑", "燏", "订", "珪", "≥",
"瘟", "耷",
"槑", "衷", "猕", "迁", "霎", "槜", "﹖", "鋈", "苹", "嫣", "祜", "李", "鄒", "噢", "萄", "仝", "纨",
"直", "悛",
"拣", "远", "诏", "圧", "躬", "蝟", "總", "眆", "筻", "硇", "鳁", "眠", "钆", "泞", "猱", "宾", "酞",
"募", "螳",
"腴", "念", "宠", "唯", "怊", "勃", "M", "兿", "蟑", "妁", "掸", "拌", "铸", "讼", "诟", "锺", "Ω",
"竟", "羚",
"剽", "C", "苦", "煳", "罢", "跨", "~", "豸", "±", "俬", "捺", "彦", "钣", "鋆", "用", "缤", "搁",
"徼", "谦",
"筘", "嗨", "扮", "旇", "折", "咯", "昆", "叟", "垂", "箐", "捻", "燕", "島", "瞀", "鮮", "屡", "點",
"瘭", "恚",
"旚", "丟", "捽", "菁", "瀑", "炕", "蹩", "芒", "r", "是", "媾", "鹝", "囵", "萤", "拷", "频", "埴",
"课", "癍",
"袱", "螯", "谘", "榛", "Y", "缣", "裔", "憩", "相", "觀", "晗", "坳", "炔", "勉", "汆", "钡", "舐",
"衫", "疫",
"鲙", "蘩", "穈", "殁", "九", "泻", "咤", "構", "谆", "陕", "装", "蔡", "画", "介", "苋", "務", "敝",
"俟", "帇",
"鸺", "贸", "茗", "肃", "滪", "输", "瘗", "菽", "饹", "诉", "遐", "浑", "扎", "卟", "铀", "邗", "觋",
"嘎", "塑",
"潏", "金", "姘", "潋", "逵", "鲻", "逯", "炮", "甄", "髡", "剩", "嗬", "芴", "屋", "改", "骣", "芪",
"邠", "痋",
"珑", "帆", "狙", "八", "奔", "族", "轵", "氖", "雕", "痧", "眊", "胛", "酉", "鲼", "砣", "猸", "餮",
"郇", "沫",
"跖", "蝉", "屑", "辘", "閣", "涑", "邡", "篃", "交", "笼", "颇", "贻", "魄", "黡", "劂", "糠", "炅",
"帨", "苍",
"瓴", "粤", "莎", "朿", "埔", "绸", "齁", "鱿", "惨", "腢", "郡", "棠", "猫", "脑", "風", "蚱", "捐",
"嵌", "胱",
"馗", "竽", "泥", "辍", "怖", "雾", "絮", "淼", "筝", "碲", "悼", "龀", "の", "珥", "忐", "溲", "昕",
"荔", "掂",
"瘦", "僭", "蔌", "抺", "椅", "誉", "扯", "僜", "停", "衉", "汇", "赔", "眄", "呙", "咙", "剿", "次",
"蛟", "嗓",
"』", "汕", "詈", "帘", "踧", "姁", "血", "堪", "喜", "滩", "璎", "胄", "俨", "眚", "凌", "拽", "滔",
"⑿", "嬃",
"―", "汐", "潭", "阡", "呓", "婷", "执", "妊", "恂", "妥", "鳘", "蔫", "设", "睒", "笪", "謇", "鞋",
"谍", "黯",
"虍", "馬", "蚧", "骑", "峤", "舾", "儀", "駡", "β", "蓑", "柏", "痒", "蒇", "痕", "妍", "熠", "僻",
"爬", "迭",
"畫", "绰", "湯", "凭", "菼", "懈", "顒", "午", "箪", "糙", "址", "钼", "堵", "佘", "侍", "卤", "(",
"榚", "泽",
"溘", "蟹", "b", "燁", "颂", "菠", "榉", "鲡", "埸", "荛", "歘", "断", "邸", "贡", "礞", "蔼", "脸",
"爪", "帜",
"翡", "仟", "皎", "辆", "滫", "昔", "™", "柬", "弓", "遇", "杪", "侨", "娓", "镪", "觑", "一", "踌",
"牟", "褡",
"厩", "晌", "每", "娘", "渤", "c", "咫", "成", "颏", "孩", "鼓", "瞌", "槁", "捒", "阉", "伉", "癣",
"胞", "鲟",
"瓤", "杅", "紡", "喂", "掠", "镜", "镧", "侞", "赦", "貝", "丕", "臧", "L", "池", "彷", "棓", "锽",
"渊", "食",
"饨", "堡", "玥", "氣", "讽", "敬", "闺", "帡", "携", "哫", "珈", "魆", "哄", "旁", "喻", "泄", "畎",
"郁", "唅",
"葜", "繪", "飐", "谶", "聆", "斝", "谥", "辉", "髅", "進", "吧", "蹀", "铛", "笛", "睥", "楼", "凝",
"況", "鸷",
"苠", "饺", "沙", "缴", "块", "梢", "慝", "珐", "鄏", "霰", "迸", "氆", "趵", "棣", "鳔", "祆", "☆",
"苯", "恁",
"螨", "庭", "缠", "槠", "津", "髋", "诔", "葶", "蜾", "坻", "蒹", "摔", "向", "垩", "蹭", "淇", "筛",
"滬", "玡",
"铺", "逼", "劵", "绲", "团", "鳀", "常", "玖", "擢", "株", "铵", "樽", "弭", "醇", "糨", "璈", "曩",
"潔", "祘",
"磨", "希", "鲅", "擂", "谗", "唳", "欷", "欧", "绋", "庙", "琬", "稳", "糊", "拥", "霪", "浼", "翎",
"俜", "摸",
"筚", "巯", "墼", "苫", "缩", "镚", "婪", "圹", "咚", "儿", "蒽", "婆", "鲐", "雹", "霞", "嶪", "濠",
"琉", "澌",
"媢", "禤", "摺", "掏", "矢", "艄", "围", "呸", "寺", "拤", "氐", "柝", "跎", "僖", "挢", "茨", "涮",
"缫", "撸",
"荨", "嶷", "廋", "魋", "付", "喋", "蜗", "邙", "棹", "璪", "倡", "鞭", "游", "錦", "眬", "抒", "眈",
"培", "夏",
"黔", "獐", "皋", "戛", "鲀", "垒", "耽", "纤", "漩", "铈", "握", "窝", "芋", "濞", "截", "零", "敖",
"眸", "怦",
"噎", "簋", "掳", "妣", "湃", "璠", "殄", "觞", "桅", "笋", "鲞", "踯", "傀", "犨", "抵", "疰", "暌",
"耖", "供",
"枳", "怂", "娶", "鸩", "捣", "庸", "逡", "懋", "颃", "長", "鼫", "姮", "蹈", "耵", "乂", "骐", "殇",
"膏", "仳",
"冥", "梭", "洵", "碣", "昝", "仉", "軒", "隍", "更", "な", "嵕", "拜", "粑", "鲴", "吇", "秃", "尕",
"魃", "狨",
"臛", "蟥", "胨", "注", "谁", "张", "才", "尸", "派", "矮", "洳", "舟", "溺", "锴", "寓", "籴", "夕",
"叭", "荠",
"澼", "劃", "久", "私", "炉", "娟", "麤", "稂", "河", "纴", "夺", "亏", "焙", "。", "塗", "蜩", "栌",
"渡", "薰",
"崋", "揿", "漤", "啾", "郏", "舣", "卉", "爱", "牚", "撵", "钺", "再", "企", "笺", "疾", "承", "俾",
"瞈", "邰",
"汾", "瘛", "檫", "蒎", "觅", "绀", "掎", "U", "赓", "匳", "聘", "蛤", "跤", "嗜", "洼", "歔", "弟",
"飕", "莼",
"嫉", "那", "滈", "践", "僦", "偎", "扢", "绚", "乕", "旳", "招", "饯", "®", "攸", "鞁", "囫", "铨",
"陒", "鷄",
"畀", "韨", "經", "纾", "萸", "肴", "→", "宗", "迳", "鳞", "亚", "搂", "喀", "狮", "坦", "瞥", "采",
"姝", "钳",
"□", "剌", "維", "葸", "鼩", "公", "刀", "沩", "喔", "泺", "哉", "徨", "篝", "掊", "沕", "运", "偆",
"浒", "语",
"乇", "仪", "萝", "疍", "踽", "碡", "熰", "荞", "嚓", "天", "饰", "泵", "械", "孑", "蛰", "荟", "源",
"峡", "矜",
"睬", "噬", "腆", "婉", "‘", "等", "誓", "辀", "岖", "琖", "碜", "霍", "怼", "唛", "弈", "淑", "疆",
"晴", "镴",
"鸡", "埚", "焕", "芦", "唻", "踅", "吴", "殡", "唏", "吨", "寡", "鹉", "絲", "坉", "會", "埭", "Ⅲ",
"捏", "墅",
"卓", "叙", "徇", "柜", "各", "荭", "J", "恝", "囐", "蓉", "犋", "叡", "莺", "颌", "蒸", "饸", "疋",
"玊", "兢",
"鱽", "藍", "杳", "辂", "獘", "拔", "侪", "湍", "膂", "渔", "瘊", "雉", "稁", "職", "僤", "鄳", "祁",
"稱", "I",
"裴", "锉", "曹", "鲶", "挨", "哑", "鷪", "鏠", "煞", "师", "蛲", "牁", "琅", "告", "媒", "祭", "确",
"荚", "亰",
"蝗", "阗", "歩", "疲", "f", "唣", "愛", "郾", "棍", "山", "狲", "纽", "蚡", "栂", "馓", "诊", "猴",
"喤", "来",
"继", "桎", "嬛", "骞", "邴", "暄", "贼", "昴", "廿", "克", "耔", "彤", "鹭", "葓", "骢", "龁", "鏡",
"瀚", "赅",
"韩", "譄", "榷", "殚", "膛", "须", "、", "砖", "唶", "番", "蛘", "畴", "铠", "亢", "氓", "铰", "炻",
"筫", "迢",
"兰", "玺", "砻", "积", "莜", "吸", "监", "膦", "迪", "迷", "冷", "哀", "贳", "瞄", "器", "鹡", "惺",
"徐", "酢",
"寒", "Ⓡ", "倾", "飞", "楽", "涢", "队", "舆", "赤", "璩", "戳", "殳", "掮", "舴", "蜷", "宄", "拴",
"癌", "舛",
"婀", "抟", "靡", "骍", "揸", "思", "慧", "平", "橘", "臭", "硖", "卬", "畈", "兠", "茸", "脂", "魚",
"晩", "御",
"龋", "涣", "罨", "爍", "糌", "汧", "缐", "贽", "要", "祀", "鲊", "爼", "獯", "瀣", "棋", "肈", "佣",
"娣", "柩",
"枸", "偃", "v", "唷", "劍", "榴", "槐", "漫", "洽", "蒡", "籼", "魔", "峋", "第", "歙", "萧", "谮",
"埯", "撮",
"马", "绡", "裘", "鹋", "蓬", "显", "噶", "倒", "镳", "艽", "窬", "拳", "樯", "跋", "詹", "钥", "心",
"嶽", "嚋",
"戎", "吕", "涂", "悃", "麦", "骋", "推", "箩", "硚", "匆", "村", "五", "杨", "凑", "鞫", "镰", "伥",
"诒", "纣",
"崃", "鸻", "翰", "辌", "廛", "證", "舢", "盼", "腿", "圳", "贱", "皿", "隆", "屈", "龏", "瓒", "顏",
"↓", "赈",
"煙", "窍", "韧", "壁", "莰", "箬", "蹋", "褰", "峥", "悚", "坜", "环", "回", "疼", "渍", "蝄", "东",
"臂", "坩",
"走", "痍", "或", "蜀", "熳", "蜻", "佐", "懿", "嚅", "紗", "螭", "忖", "顶", "狡", "吲", "洣", "帛",
"呶", "柞",
"柫", "酿", "粥", "琢", "呵", "踝", "榀", "呲", "價", "鼋", "欺", "此", "背", "猎", "昱", "濡", "稚",
"欠", "暇",
"茬", "牙", "迹", "尼", "氛", "膠", "缯", "娼", "骚", "姒", "鬟", "霁", "鲔", "者", "驰", "倩", "馉",
"工", "芬",
"烙", "卦", "C", "裂", "垲", "摆", "珮", "缏", "杞", "绘", "司", "如", "姞", "荆", "挖", "跗", "伍",
"氚", "钘",
"郢", "轱", "篆", "吭", "夡", "鹫", "讷", "轺", "!", "匈", "待", "聱", "黏", "海", "蹶", "趋", "鎮",
"觊", "江",
"咸", "富", "艴", "稗", "钜", "搏", "壶", "鲮", "薪", "猞", "轰", "踪", "赣", "循", "序", "噻", "若",
"裾", "许",
"癞", "吓", "判", "踔", "查", "蚀", "[", "樓", "坌", "岳", "榄", "役", "倜", "⒂", "旭", "溆", "惯",
"咀", "跫",
"选", "囱", "污", "镶", "⒁", "淠", "氮", "酯", "寅", "芼", "炊", "夯", "郪", "农", "褲", "嘬", "蹻",
"烔", "罄",
"开", "靴", "镇", "杯", "羰", "硪", "籍", "摘", "馀", "餐", "眯", "⑴", "呗", "巫", "幤", "蒤", "蒗",
"镥", "檵",
"盛", "純", "娃", "●", "耿", "巡", "婴", "槔", "i", "颊", "Ⅳ", "栅", "绅", "邘", "冉", "碧", "使",
"熨", "羞",
"扼", "漳", "觯", "楊", "励", "逑", "咄", "之", "斤", "嘣", "鹰", "媸", "鲂", "褚", "磚", "琨", "聪",
"牖", "太",
"蓍", "涫", "≤", "虽", "鸽", "燧", "褊", "聿", "壬", "然", "疚", "莲", "悴", "簃", "颓", "坠", "瞬",
"汳", "l",
"登", "瘼", "窳", "桤", "縯", "匣", "坡", "↑", "愦", "攘", "渭", "嬢", "鲰", "性", "楚", "澈", "赪",
"達", "鄯",
"罅", "帽", "茠", "底", "嫜", "奏", "浅", "荽", "楹", "鼍", "枵", "嗔", "滍", "椴", "嵩", "氤", "搠",
"两", "榔",
"树", "吝", "基", "峂", "栎", "侮", "舸", "遂", "颡", "锷", "杼", "酔", "幄", "哽", "睢", "陔", "※",
"嚆", "宬",
"宽", "髦", "笾", "保", "蹊", "榕", "咏", "椋", "丧", "裤", "骛", "逧", "弇", "崆", "樘", "疤", "鸤",
"伞", "抚",
"诎", "诵", "豢", "佳", "差", "埝", "极", "黍", "煜", "曰", "阱", "悞", "叹", "垤", "藁", "嗵", "崔",
"卫", "珂",
"憯", "蔬", "菜", "碑", "扈", "铆", "夹", "衡", "弱", "挈", "徜", "疠", "丶", "遠", "提", "斧", "炟",
"肺", "B",
"她", "晟", "谎", "邱", "粳", "酽", "爨", "鬼", "伧", "兹", "嶓", "谤", "饕", "揶", "谱", "歡", "髪",
"餍", "泳",
"郞", "谣", "汉", "褐", "非", "刽", "缅", "饴", "齐", "兴", "涯", "芫", "凡", "褶", "晡", "努", "蚶",
"彥", "皤",
"砌", "黼", "吹", "指", "㙟", "蓁", "鹜", "話", "拊", "辨", "盎", "肌", "旘", "软", "颍", "甏", "滚",
"旦", "滨",
"间", "尴", "对", "鄘", "称", "镗", "咅", "璐", "怔", "垛", "洎", "瓮", "绨", "脚", "遒", "吊", "纸",
"蹅", "经",
"泉", "武", "汀", "歪", "败", "拾", "铪", "吼", "邹", "磊", "论", "岛", "厍", "锛", "芎", "芭", "音",
"澧", "镕",
"锒", "宙", "牵", "忱", "嫔", "麯", "澉", "擐", "砥", "撞", "痴", "盹", "畿", "厾", "酸", "俑", "脽",
"鸈", "枷",
"咨", "蔹", "诂", "胰", "董", "脶", "黩", "髓", "鉵", "澎", "鲽", "梧", "樱", "诜", "鲯", "跂", "盂",
"浴", "苻",
"锅", "實", "碁", "嘛", "氕", "艮", "涟", "绢", "姿", "茝", "砘", "簿", "穷", "镃", "∈", "抽", "事",
"誜", "窅",
"瀘", "鲹", "兖", "嵎", "陧", "榍", "轶", "柿", "藤", "薏", "娆", "骷", "梅", "摒", "睪", "剪", "羸",
"忧", "邝",
"跺", "旆", "堕", "伫", "绍", "疵", "樟", "–", "绾", "蜴", "靸", "侃", "瘘", "珧", "遨", "縠", "信",
"充", "桔",
"黇", "劬", "脒", "良", "俵", "颙", "轹", "犿", "屐", "牾", "4", "兮", "澝", "汗", "沼", "铲", "濋",
"鹬", "丝",
"妫", "重", "蒺", "磲", "曚", "尔", "国", "桐", "俣", "剐", "哼", "恹", "哧", "藔", "谓", "轨", "眩",
"痞", "添",
"鬯", "库", "梱", "婕", "蜢", "贿", "敕", "泯", "羟", "龇", "垸", "左", "肖", "辎", "鞣", "谄", "可",
"腺", "末",
"狞", "贷", "嗌", "仕", "楞", "膻", "臻", "欻", "洲", "所", "檀", "抔", "罹", "牒", "仫", "芨", "柄",
"嫩", "酒",
"祙", "渠", "的", "笨", "鳐", "楡", "过", "苡", "核", "拖", "阢", "莒", "凤", "锋", "` + "`" + `", "硎", "弁",
"鬶", "朐",
"忏", "於", "昊", "剟", "咳", "湘", "日", "满", "哨", "螵", "餪", "放", "佶", "葵", "硷", "c", "抱",
"锥", "芮",
"啻", "惊", "峁", "琊", "嶲", "撺", "煅", "屏", "袗", "鄞", "梓", "鹌", "宅", "赂", "鱼", "洱", "騳",
"E", "物",
"觏", "雙", "瑀", "上", "淩", "愀", "❋", "鄙", "憝", "沛", "硫", "产", "垯", "亁", "枭", "堰", "赑",
"趾", "庹",
"腭", "迨", "拚", "晒", "蜇", "扣", "纰", "闵", "窭", "椽", "菏", "嘁", "伛", "郸", "素", "殷", "表",
"躞", "笸",
"耻", "荧", "辛", "篑", "馈", "壮", "耩", "宛", "慰", "盡", "塆", "铯", "苏", "王", "桕", "⑧", "°",
"浚", "栉",
"朘", "虚", "骆", "坂", "秤", "鲋", "蕊", "渝", "呦", "潼", "驱", "诼", "峇", "盤", "趴", "肄", "笑",
"讹", "貋",
"穂", "啼", "趟", "暽", "傣", "蜎", "挎", "陳", "勖", "戴", "旃", "瞎", "舌", "幻", "喾", "赁", "E",
"播", "诀",
"蟛", "鹛", "骶", "輸", "連", "醳", "逅", "奉", "崖", "娩", "幔", "佃", "扅", "阔", "生", "贬", "疯",
"珀", "苶",
"屯", "裣", "蹯", "蝮", "解", "陂", "疝", "茈", "帑", "议", "仲", "埙", "竺", "峰", "遮", "涎", "穸",
"阂", "潵",
"镱", "例", "荑", "u", "脎", "衍", "轲", "⑵", "虾", "颚", "钞", "²", "伴", "根", "沣", "腌", "户",
"~", "辙",
"愧", "噤", "觥", "波", "铗", "纂", "鲺", "僚", "毐", "〇", "桼", "祗", "慢", "啵", "坏", "吗", "嗞",
"甬", "曈",
"徹", "灏", "混", "渌", "括", "脖", "汝", "現", "訇", "紅", "飘", "虢", "腱", "旄", "嬴", "昨", "孀",
"蚁", "呛",
"讳", "病", ",", "喈", "蒋", "镭", "葩", "耲", "鳈", "锄", "喘", "返", "傕", "咆", "享", "枥", "瓠",
"茳", "铱",
"脘", "暹", "廒", "爝", "橹", "瞑", "铎", "岢", "叁", "翏", "捭", "賀", "悉", "帝", "芥", "牀", "闌",
"毯", "亍",
"弧", "锆", "币", "祊", "纔", "齑", "肟", "绤", "獨", "翚", "颢", "係", "鍪", "粉", "统", "诗", "娜",
"褥", "鈺",
"湔", "呤", "犸", "湨", "泣", "蟾", "犾", "烛", "斐", "朦", "室", "诨", "榭", "煦", "醺", "敞", "燮",
"糅", "衽",
"孔", "猄", "疭", "辰", "钽", "胁", "釆", "钉", "胤", "涧", "弼", "濯", "汨", "颖", "茫", "皑", "遏",
"捃", "坭",
"燴", "肩", "滞", "玢", "巽", "砺", "蜿", "毁", "億", "骥", "本", "忽", "肚", "搽", "靰", "郴", "跆",
"客", "酣",
"α", "屎", "辩", "殂", "垝", "紫", "秦", "喇", "凶", "傧", "铐", "蘊", "補", "贤", "竿", "途", "慗",
"榖", "券",
"莠", "逆", "鳇", "误", "崟", "妇", "磷", "捧", "莸", "⇋", "绺", "稻", "填", "逋", "侈", "隶", "侵",
"翥", "惘",
"惧", "鸥", "赠", "壳", "芯", "巩", "獗", "硅", "搎", "鲛", "9", "夸", "穆", "缜", "诓", "观", "薛",
"咎", "杧",
"页", "饫", "瑟", "率", "礤", "悭", "畔", "匯", "匮", "鼠", "犒", "芡", "傍", "嫂", "啸", "鄉", "哭",
"鄱", "捷",
"靺", "嚒", "嘀", "哒", "#", "拼", "钚", "魁", "霣", "眶", "郊", "死", "愁", "箭", "鼙", "签", "害",
"斛", "睑",
"蟜", "余", "墨", "様", "读", "養", "貉", "较", "浆", "翩", "徂", "冕", "铧", "列", "诈", "穝", "缑",
"纲", "志",
"舀", "甾", "举", "馁", "ä", "畹", "榼", "垢", "襁", "麟", "灭", "佴", "镩", "酝", "柒", "梯", "傈",
"萭", "悫",
"莨", "搞", "+", "兄", "偲", "攀", "曝", "嵝", "喳", "从", "遶", "撴", ".", "鄄", "欲", "挺", "娡",
"发", "速",
"胲", "褀", "态", "行", "蚓", "坼", "适", "厦", "寐", "带", "緃", "醤", "珽", "‧", "溍", "斋", "鐀",
"朝", "欢",
"传", "築", "咪", "据", "蹜", "医", "妄", "肇", "囝", "怡", "镎", "桩", "轩", "岔", "腐", "矽", "媵",
"搒", "菔",
"拘", "D", "欃", "唧", "瞒", "郈", "绦", "吟", "撝", "醉", "镣", "匝", "拎", "砒", "顸", "袁", "驼",
"愔", "实",
"國", "奧", "胩", "府", "逾", "愕", "廷", "碌", "锖", "狩", "褴", "镢", "芷", "娥", "唤", "┌", "云",
"О", "檔",
"驴", "躯", "驺", "洃", "檑", "窴", "(", "腕", "立", "楯", "齮", "〔", "漆", "k", "芍", "蹽", "鬓",
"概", "楣",
"唐", "闲", "糗", "旱", "幸", "腽", "嗄", "迂", "镠", "顿", "扥", "圃", "烜", "馍", "佝", "岷", "童",
"悦", "┐",
"铌", "袈", "靓", "骸", "和", "乔", "灸", "泓", "临", "睿", "掖", "偿", "鐘", "犁", "祓", "鈴", "搌",
"授", "鹳",
"赢", "怅", "絪", "硬", "芙", "螅", "”", "傢", "避", "裕", "歁", "全", "衰", "仃", "媛", "鬻", "跽",
"沌", "急",
"猷", "激", "巉", "哝", "渣", "笫", "跳", "螫", "熜", "Z", "筷", "佩", "啶", "萃", "頫", "荙", "出",
"孽", "钟",
"戡", "釉", "咬", "滦", "鹇", "贯", "鹮", "具", "翁", "机", "濱", "谳", "釣", "懑", "葛", "袯", "谭",
"质", "胴",
"誊", "侗", "⑩", "静", "蚜", "溋", "嫪", "嗲", "瑭", "座", "舫", "靶", "棘", "泊", "嵖", "摧", "勋",
"僡", "藉",
"疖", "巂", "随", "罾", "崚", "猹", "憨", "苘", "斓", "鼷", "利", "谲", "剔", "艺", "箓", "蛀", "鲚",
"搐", "裟",
"捶", "绌", "揪", "帮", "缥", "匍", "冀", "杻", "逛", "邑", "禾", "郰", "黜", "丘", "樂", "滌", "緣",
"胃", "苄",
"巾", "瑜", "元", "蝶", "层", "烧", "级", "岭", "蘭", "繇", "蝓", "洞", "奢", "则", "政", "矾", "啭",
"瘠", "碴",
"忤", "身", "匠", "警", "饩", "犬", "皲", "箔", "豕", "虑", "草", "喟", "芤", "逭", "艳", "幡", "姚",
"賓", "饪",
"卯", "敌", "烽", "嫚", "黝", "豺", "㭗", "教", "偕", "板", "茹", "孤", "人", "狻", "寰", "厕", "玲",
"璨", "锵",
"搛", "勍", "匾", "聃", "奘", "垃", "焓", "喽", "嫫", "貌", "瘐", "嚰", "孟", "衔", "郎", "账", "础",
"电", "黑",
"骁", "拨", "濆", "圉", "刮", "闭", "竣", "铅", "羔", "硌", "筑", "难", "管", "苕", "眺", "嫄", "竖",
"榟", "崴",
"摭", "狐", "娑", "②", "罽", "谊", "←", "狳", "铫", "凯", "狉", "9", "肪", "崤", "莊", "妨", "缶",
"滃", "瀦",
"揉", "肫", "恧", "糯", "嵬", "5", "裆", "嚷", "稣", "隐", "仂", "て", "驹", "籽", "肢", "尘", "苈",
"撷", "镲",
"趹", "晤", "唱", "鉏", "篌", "驩", "雍", "闳", "拄", "藜", "朴", "伺", "诳", "房", "吱", "Й", "鳄",
"罿", "祧",
"酩", "郅", "耎", "尜", "绝", "禅", "揠", "鎏", "慕", "麥", "呜", "鸫", "党", "尝", "砑", "牌", "踉",
"刨", "襻",
"㾄", "螽", "谌", "止", "抑", "爻", "磬", "铄", "蓠", "委", "汲", "鹑", "╱", "嚣", "彝", "穄", "穹",
"態", "醋",
"⒀", "叼", "婳", "簌", "渥", "很", "甸", "帅", "锏", "与", "樾", "泷", "棼", "湲", "越", "祥", "短",
"顼", "阘",
"宋", "馘", "鈉", "未", "囍", "浏", "叻", "箜", "鑽", "法", "曲", "淤", "僮", "做", "强", "析", "磕",
"谠", "染",
"促", "朊", "隼", "铉", "莆", "蝣", "孛", "薮", "s", "惴", "秘", "妩", "訄", "蔓", "喷", "诡", "犷",
"酐", "酇",
"刹", "壅", "甫", "史", "孃", "髌", "螬", "擤", "漏", "寞", "奡", "悢", "颔", "岁", "耄", ";", "又",
"锭", "鲤",
"癔", "杰", "孥", "酲", "蓐", "耋", "捆", "庖", "面", "鹈", "殊", "剡", "峪", "识", "锨", "归", "茴",
"—", "菤",
"汁", "攝", "液", "鼐", "示", "讠", "男", "凍", "ò", "明", "莓", "砜", "崎", "蜂", "斡", "榫", "娅",
"钪", "昙",
"胜", "欣", "怨", "◆", "粗", "秷", "节", "市", "贩", "祟", "弍", "蒟", "烁", "糧", "蠃", "編", "黙",
"壕", "戚",
"犊", "桥", "仺", "孳", "怯", "皓", "倆", "垮", "扩", "诮", "钝", "脯", "晏", "帔", "葫", "瑾", "運",
"孬", "跄",
"掣", "癜", "掌", "墀", "禇", "耸", "蜓", "鹆", "鄢", "攰", "瘢", "暝", "鸣", "峧", "遵", "笃", "畚",
"帧", "晨",
"镔", "搜", "靠", "咐", "韓", "绮", "觉", "拦", "斲", "疽", "掐", "尽", "許", "矶", "镉", "豹", "粞",
"袋", "酵",
"蛙", "戕", "劉", "髀", "彭", "玎", "囿", "郐", "善", "睃", "結", "拧", "邯", "讧", "召", "椭", "瑪",
"痼", "庼",
"反", "疱", "屠", "荣", "君", "胍", "乙", "臬", "头", "诰", "讪", "席", "晁", ":", "理", "槿", "璘",
"禧", "呢",
"蹙", "擒", "鸲", "丐", "苓", "壑", "滥", "⑾", "炗", "礴", "耕", "卅", "唿", "苛", "寵", "窖", "麻",
"蕨", "沤",
"氢", "虔", "癃", "及", "崛", "爽", "蛔", "颤", "膲", "桢", "坐", "蟞", "儇", "葚", "骤", "誤", "寝",
"嘭", "灰",
"汹", "韂", "铮", "慒", "寶", "肽", "摅", "紧", "亞", "潸", "悯", "橛", "檗", "闹", "愿", "担", "袄",
"棚", "垟",
"塄", "婞", "麈", "麸", "暗", "咦", "跞", "谡", "盈", "磐", "慎", "瘰", "掼", "憔", "研", "被", "贮",
"莛", "至",
"呀", "庑", "矫", "摛", "怃", "缙", "磺", "即", "驻", "瘤", "偏", "℃", "嫘", "癫", "汈", "鹟", "搅",
"辅", "璀",
"阊", "绻", "瑙", "蓂", "棺", "孢", "铊", "鼒", "果", "砮", "飾", "凰", "Я", "遗", "祛", "纮", "劲",
"霹", "骃",
"绔", "薅", "瀵", "垅", "?", "轻", "惇", "怕", "啥", "哙", "燎", "缆", "匡", "怫", "卞", "朋", "酏",
"阑", "爾",
"伏", "敏", "埼", "罩", "菹", "艋", "肭", "鯭", "杋", "裀", "撬", "蕺", "惠", "大", "爇", "笈", "絷",
"琳", "谫",
"诛", "糇", "袢", "倓", "髃", "觽", "埏", "寖", "個", "筴", "外", "漯", "樭", "喁", "杀", "臑", "缇",
"裸", "巅",
"毹", "茅", "忆", "琼", "唑", "烷", "项", "隋", "约", "排", "吮", "谂", "宝", "牲", "瘫", "娄", "沂",
"醫", "拭",
"纺", "蹰", "哞", "风", "霆", "值", "酺", "侠", "螾", "埂", "育", "夷", "鮼", "怍", "鸠", "Θ", "瞳",
"阇", "耥",
"羝", "伽", "洴", "記", "楔", "颼", "沪", "邢", "冰", "昀", "阙", "洌", "嫦", "杂", "仔", "芑", "潴",
"痄", "桨",
"连", "碓", "塈", "F", "昇", "何", "桦", "晥", "驵", "旋", "药", "银", "奋", "灣", "俐", "絡", "嫁",
"浮", "为",
"鞅", "科", "颦", "潽", "镍", "鸨", "粵", "骂", "拱", "韫", "盆", "赎", "尿", "钿", "坍", "唁", "秧",
"昌", "曆",
"颋", "遭", "秭", "褔", "腋", "〉", "吉", "漓", "臆", "焘", "已", "制", "钹", "鴨", "咖", "莘", "P",
"碥", "互",
"治", "标", "膝", "伪", "浿", "纛", "郗", "看", "佧", "糖", "篓", "亡", "´", "骙", "澡", "影", "窂",
"紬", "镅",
"慌", "框", "晋", "説", "丢", "凹", "卖", "巧", "蹉", "乾", "莫", "Z", "谔", "矧", "铑", "暴", "庄",
"湿", "活",
"穿", "腩", "筣", "水", "6", "琦", "迈", "伯", "洄", "抡", "▪", "酋", "荤", "雒", "粕", "簠", "菰",
"髁", "枇",
"陲", "多", "仗", "央", "滁", "胸", "梏", "痉", "姑", "襞", "﹑", "齿", "弩", "花", "吆", "赫", "岵",
"佪", "谑",
"锤", "轴", "盐", "馄", "臜", "戢", "涠", "鸸", "糟", "孪", "禁", "蒲", "化", "疏", "痰", "脾", "刈",
"應", "珍",
"膺", "扌", "廙", "汜", "牍", "虐", "婿", "啕", "彻", "赝", "陶", "蠲", ">", "位", "屁", "醍", "粢",
"挪", "臌",
"滹", "遴", "馨", "n", "稼", "徊", "酌", "轸", "债", "朰", "程", "辞", "痊", "插", "鹩", "郄", "铝",
"狱", "叱",
"同", "寄", "搪", "蚯", "魭", "舍", "旷", "闰", "涝", "民", "嗡", "苌", "馕", "姥", "屉", "啧", "枢",
"❤", "窕",
"钊", "矬", "菂", "佑", "≠", "獬", "桁", "墟", "皖", "鼻", "它", "歇", "独", "好", "晕", "蚝", "锞",
"颈", "豚",
"聖", "裉", "扫", "岿", "悒", "佥", "苗", "妞", "晚", "圭", "茼", "脲", "摊", "窠", "狸", "抻", "场",
"呼", "囟",
"噗", "狺", "困", "瀹", "削", "衬", "谰", "蛆", "訓", "鉄", "痃", "炱", "蝻", "我", "暨", "骓", "馋",
"埤", "脞",
"晃", "螟", "洮", "泛", "掾", "穑", "米", "蕲", "玦", "讙", "逢", "劐", "袭", "凫", "僳", "畛", "晷",
"鳕", "Ë",
"愬", "坫", "鳡", "鞯", "叔", "胂", "囚", "筋", "青", "度", "涕", "琰", "﹔", "径", "陇", "睛", "链",
"状", "逶",
"蘅", "“", "庇", "邽", "纥", "踶", "爺", "狭", "钫", "桃", "弛", "淳", "办", "茕", "砸", "喱", "仅",
"潞", "杈",
"得", "咕", "俞", "检", "借", "恋", "驿", "倌", "钢", "琐", "哆", "撙", "箫", "川", "猥", "牢", "蹁",
"城", "馏",
"锡", "楝", "蛱", "奈", "瑶", "桺", "耆", "翟", "阒", "稲", "橐", "萱", "惹", "蘼", "主", "擦", "蟒",
"台", "佬",
"荫", "廖", "笏", "铕", "衣", "洇", "炒", "瀍", "崭", "圻", "洚", "契", "嫱", "倏", "晶", "了", "堠",
"勰", "椎",
"询", "梗", "飒", "锰", "览", "溇", "寻", "蓅", "【", "碇", "井", "露", "顔", "堌", "庳", "踩", "i",
"饷", "俊",
"楫", "條", "搭", "奍", "羽", "憋", "岘", "毡", "曜", "乃", "′", "针", "羲", "菓", "吩", "咩", "鞘",
"尊", "宫",
"舜", "啖", "惗", "北", "懊", "骇", "阄", "躅", "权", "缲", "肥", "铜", "《", "录", "也", "棬", "煮",
"舄", "厮",
"'", "順", "受", "霜", "新", "售", "牞", "圣", "妗", "犴", "宥", "哦", "陀", "卺", "冚", "蹒", "亸",
"禮", "骰",
"瑢", "弒", "抛", "谷", "嫰", "動", "嘌", "惩", "枣", "忌", "茡", "爵", "嘚", "郧", "丨", "敲", "帚",
"沭", "槊",
"⑶", "專", "毶", "圄", "磅", "蛭", "由", "蠹", "剜", "诫", "秆", "愠", "藓", "母", "请", "衩", "忸",
"蜕", "饽",
"晦", "倔", "腠", "痛", "品", "簧", "父", "锐", "描", "蓰", "蛴", "箍", "兕", "苜", "饼", "奚", "泗",
"裥", "皂",
"嵚", ",", "澶", "蠖", "沅", "馎", "籀", "菝", "眵", "糥", "铽", "痤", "颟", "淄", "作", "抉", "俄",
"么", "郑",
"耒", "佛", "1", "纡", "鸢", "④", "鎚", "壖", "遢", "鬈", "拢", "托", "哈", "節", "橦", "冼", "六",
"耗", "樵",
"涔", "舳", "龌", "衿", "婧", "栓", "椹", "嘘", "膊", "茁", "丹", "螃", "剖", "洧", "珞", "潺", "孱",
"呐", "萩",
"刷", "引", "说", "熟", "/", "靖", "酷", "耠", "饬", "菌", "洙", "荃", "饲", "酾", "阁", "陬", "铿",
"倻", "牮",
"鞡", "撕", "倘", "盒", "曺", "襦", "辄", "算", "塬", "潢", "羖", "湾", "续", "△", "疙", "谖", "嘅",
"遑", "篚",
"筮", "氍", "递", "尧", "G", "{", "分", "埒", "@", "蜍", "荼", "襆", "槭", "檠", "縢", "濉", "梆",
"隔", "镛",
"倞", "润", "瓯", "瓢", "蟊", "沐", "啷", "砚", "皱", "剅", "儙", "错", "幌", "滓", "砗", "郤", "喧",
"峣", "簸",
"毖", "踏", "锕", "…", "悖", "谧", "醵", "加", "镐", "泐", "傫", "胪", "缄", "卩", "蓼", "丸", "垌",
"汞", "宴",
"膙", "圊", "矻", "嚏", "漾", "幕", "駕", "葒", "绪", "袪", "镋", "杭", "澴", "鬃", "粟", "偻", "饳",
"抨", "亟",
"温", "韶", "轿", "罟", "际", "诖", "复", "坯", "骗", "*", "副", "裢", "憬", "邾", "崇", "蕈", "疮",
"粽", "炝",
"珲", "莅", "衾", "爲", "枯", "汛", "仁", "熏", "馥", "㎡", "檐", "锦", "竭", "颁", "遽", "瘙", "样",
"遛", "殍",
"湄", "消", "鳌", "痫", "鳏", "瓶", "窈", "谚", "麒", "鸹", "蟋", "横", "唠", "瘪", "媪", "侔", "鐵",
"系", "杖",
"m", "叉", "沟", "衢", "寘", "■", "弗", "建", "疣", "珣", "綦", "劈", "道", "嘈", "先", "芝", "降",
"滕", "邵",
"邺", "給", ")", "廨", "郛", "势", "氇", "坤", "昂", "焼", "奕", "闱", "朓", "毽", "还", "坨", "銭",
"龂", "銎",
"壽", "矸", "窒", "①", "玷", "蝽", "泃", "烀", "魈", "★", "慶", "K", "嘶", "酶", "呖", "殿", "乡",
"䄂", "阳",
"轪", "碱", "譬", "摩", "鳖", "刳", "地", "包", "貊", "悝", "圩", "今", "嚭", "凳", "谕", "馃", "捎",
"佯", "侬",
"愆", "微", "涤", "舔", "蛇", "筲", "助", "锾", "剧", "缧", "簪", "惚", "柢", "庾", "虹", "雪", "猡",
"脔", "亶",
"烨", "T", "锗", "芈", "女", "动", "偬", "琥", "县", "诣", "精", "嬗", "栀", "艨", "智", "冗", "闼",
"嗝", "z",
"夢", "拿", "鹲", "尤", "啮", "﹐", "ɔ", "钓", "施", "萼", "邻", "竞", "碶", "艰", "》", "翻", "馆",
"橪", "逝",
"臀", "淫", "枉", "羿", "拇", "溷", "徒", "涓", "關", "聋", "嵊", "殖", "叛", "敫", "舵", "亊", "诽",
"菱", "苎",
"破", "腚", "A", "嵋", "扊", "挂", "篷", "棂", "碟", "復", "劾", "韪", "疔", "粒", "鲵", "毙", "店",
"锻", "衮",
"寳", "◎", "斯", "倦", "醢", "曾", "茚", "荐", "隗", "芊", "豪", "亻", "哂", "堃", "宇", "桑", "匋",
"植", "亥",
"撂", "棒", "蟠", "W", "迟", "蚋", "溊", "缌", "鞚", "蚤", "適", "赌", "卣", "厚", "鲾", "匙", "槃",
"郎", "鬏",
"玳", "龄", "丈", "圮", "冑", "院", "葬", "嵐", "瓦", "孵", "漶", "星", "吐", "獍", "藠", "萍", "振",
"潜", "龉",
"匦", "粹", "諾", "畵", "峦", "&", "埕", "朵", "戒", "炳", "酪", "绂", "篁", "测", "殆", "涌", "业",
"盏", "醊",
"笆", "孰", "骊", "湛", "踰", "汎", "哲", "澙", "鲷", "√", "鄣", "亿", "螺", "吠", "伟", "凛", "骡",
"恻", "巨",
"扶", "泡", "峯", "韵", "腎", "睦", "栖", "}", "笙", "疌", "绶", "忒", "哥", "价", "纻", "薨", "漂",
"濮", "缮",
"勐", "妮", "傩", "陛", "陷", "柆", "瞭", "鲳", "烬", "喉", "固", "桡", "聊", "逦", "猊", "梻", "涵",
"栒", "逍",
"饥", "凼", "早", "姣", "蕤", "塌", "桀", "亳", "虻", "鹨", "典", "情", "怄", "商", "钍", "赚", "塥",
"煽", "垱",
"蝴", "乓", "籁", "帷", "锢", "圪", "快", "赘", "杵", "漠", "滴", "斩", "拈", "蚕", "陽", "篡", "郦",
"瞻", "郯",
"鳍", "幽", "旅", "乖", "鹖", "斫", "痂", "肸", "右", "锂", "永", "泾", "茎", "觱", "彼", "擎", "䨱",
"翱", "徝",
"醅", "求", "湫", "転", "溴", "師", "瓣", "蝠", "铭", "社", "苞", "仇", "噌", "你", "嗾", "雳", "榧",
"駹", "雯",
"叨", "遫", "氏", "航", "辗", "溢", "历", "楷", "诱", "雏", "梳", "藕", "屺", "槎", "钐", "燘", "棽",
"驸", "褪",
"清", "十", "廰", "移", "筌", "揾", "瞠", "姽", "馑", "恢", "逸", "p", "瑚", "茄", "鹧", "俗", "璟",
"栊", "买",
"瀛", "镒", "球", "氲", "缛", "講", "胀", "焒", "悲", "翕", "拗", "T", "桌", "脓", "闪", "稀", "狎",
"火", "柁",
"琴", "澍", "嗟", "龚", "楮", "噼", "隽", "栩", "焻", "哩", "藻", "瘸", "含", "偶", "界", "嘃", "昶",
"澄", "頤",
"绒", "鲁", "麝", "决", "撒", "岙", "季", "刿", "肝", "蒉", "蓇", "财", "完", "蠔", "脉", "肱", "谙",
"蜮", "郭",
"慨", "晔", "髂", "蛏", "眨", "钗", "葺", "惆", "娈", "瞵", "踞", "棁", "蝢", "嚎", "猝", "必", "剞",
"关", "咛",
"劫", "闸", "肯", "№", "莩", "哇", "蛑", "镬", "羡", "驊", "茂", "塍", "沓", "筱", "杉", "战", "茧",
"耙", "击",
"需", "腊", "酎", "畦", "葙", "鹘", "韭", "嚚", "争", "域", "伢", "鞲", "哳", "栲", "某", "翌", "哗",
"焚", "螗",
"懲", "躲", "約", "镖", "凿", "饶", "够", "剁", "铥", "应", "署", "杮", "蒂", " ", "坷", "礅", "款",
"梁", "鄜",
"髹", "選", "伤", "路", "З", "亲", "野", "啦", "捯", "憷", "鲩", "札", "怏", "塘", "绊", "愍", "簦",
"牦", "黥",
"鳜", "唉", "W", "沱", "蚺", "甪", "摉", "协", "耨", "娱", "桄", "仆", "类", "搡", "滤", "岗", "休",
"坶", "谒",
"忭", "飨", "闷", "菟", "鲣", "驷", "湜", "疡", "蚩", "萊", "䝉", "硒", "贺", "弃", "徘", "陨", "否",
"遥", "妒",
"X", "間", "觜", "跬", "夬", "羮", "喙", "赇", "鹗", "『", "砀", "残", "绿", "小", "勘", "瀌", "扉",
"耧", "衅",
"挟", "乐", "鹏", "墁", "澜", "噍", "坊", "術", "嗖", "知", "盉", "圆", "嗈", "蘖", "资", "爭", "=",
"刑", "裒",
"〈", "淸", "定", "袒", "戗", "钤", "吵", "旯", "蓝", "裎", "溅", "贰", "荏", "甥", "悌", "勤", "炽",
"换", "躜",
"!", "薄", "痱", "双", "匕", "肷", "挥", "茑", "船", "砝", "煤", "荜", "弘", "▏", "陆", "稔", "朽",
"冤", "頉",
"遊", "砰", "迎", "碎", "唪", "醪", "稆", "练", "锸", "阵", "皇", "香", "镀", "嫡", "持", "桶", "垄",
"阍", "戥",
"臣", "琛", "涘", "惶", "赙", "葆", "住", "舊", "枝", "媲", "蓣", "龅", "搦", "_", "图", "力", "纪",
"悍", "麗",
"戽", "腧", "绣", "跟", "哕", "打", "蝰", "Φ", "吞", "功", "夀", "劓", "沇", "熔", "占", "隰", "命",
"佻", "豁",
"苣", "楦", "掇", "蛛", "唢", "郜", "霉", "鲏", "予", "沸", "殻", "俯", "探", "篪", "荇", "邈", "烯",
"忮", "伸",
"岬", "×", "锧", "窸", "毪", "纩", "蛋", "讯", "骼", "叶", "楂", "犟", "站", "盘", "隈", "喝", "儣",
"兵", "尚",
"孙", "爿", "芜", "羁", "旖", "溽", "迩", "京", "7", "龃", "狝", "缦", "缁", "鲃", "怒", "故", "據",
"枫", "髙",
"亭", "耳", "飚", "O", "编", "箸", "幼", "氘", "鞮", "匐", "祯", "臃", "辫", "磋", "溝", "墙", "诚",
"阻", "档",
"歆", "璃", "悻", "婤", "映", "瑞", "牂", "话", "忠", "潘", "惋", "冬", "氦", "腔", "胬", "盔", "\"",
"饮", "贶",
"嚄", "儆", "溜", "砷", "樇", "跏", "泩", "馌", "埃", "莙", "革", "珙", "乌", "鍋", "穴", "石", "珺",
"熹", "诞",
"<", "腉", "姊", "钧", "罪", "拆", "赊", "殒", "堇", "仑", "掺", "塃", "獴", "迥", "盦", "檬", "益",
"居", "鼑",
"异", "嘻", "悔", "旮", "况", "時", "阋", "洛", "線", "#", "型", "迕", "睇", "橱", "笊", "蛞", "愚",
"茉", "镈",
"镞", "垭", "扁", "泫", "搬", "古", "书", "疸", "痨", "黟", "墉", "料", "并", "ㆍ", "裳", "鞑", "湮",
"柠", "颐",
"形", "━", "逹", "硁", "置", "韦", "瓞", "象", "殽", "均", "浓", "瞓", "椐", "洨", "乱", "襜", "终",
"優", "睹",
"敦", "鼬", "唆", "佼", "財", "瘃", "H", "痳", "勺", "依", "虎", "蕖", "玄", "缓", "滢", "^", "骅",
"诘", "弋",
":", "∩", "廪", "缈", "造", "蕉", "孖", "嫒", "寨", "意", "岽", "庶", "罗", "瞢", "酹", "蔟", "赴",
"烂", "栋",
"格", "矛", "驯", "词", "嗦", "剀", "蓓", "期", "鏢", "羑", "奴", "椱", "A", "狗", "烟", "蹬", "案",
"记", "讴",
"鳑", "侯", "霏", "焜", "沬", "份", "酦", "芗", "庚", "瑗", "鹎", "穗", "鲠", "肛", "厄", "蜔", "學",
"伊", "⑥",
"琪", "邒", "少", "霖", "蓖", "猜", "塾", "肾", "罃", "伐", "钩", "骈", "溟", "饵", "莉", "é", "刖",
"洯", "堉",
"锝", "趔", "七", "萁", "竹", "憾", "蚨", "离", "柔", "替", "侑", "飙", "气", "震", "厥", "备", "刻",
"顽", "瞽",
"腄", "雄", "燃", "旬", "简", "翠", "熥", "◇", "吃", "囡", "玙", "铷", "暖", "配", "傻", "窄", "皈",
"夼", "舂",
"乜", "苩", "攉", "雠", "茇", "锈", "酰", "粮", "祝", "考", "堍", "鳅", "彬", "▲", "孝", "蠊", "顇",
"娲", "腥",
"$", "珠", "厂", "诠", "蹓", "轼", "嵫", "捩", "硗", "胺", "证", "膀", "」", "胯", "钷", "毂", "柙",
"深", "沄",
"匹", "8", "爷", "礳", "秏", "窜", "魑", "d", "转", "烆", "屿", "眙", "極", "袤", "護", "V", "狂",
"柑", "玠",
"氩", "’", "馊", "玛", "坢", "%", "燔", "颗", "舅", "暂", "艾", "芹", "溏", "晰", "件", "琚", "仿",
"祾", "酤",
"騠", "揳", "鲫", "蜥", "仨", "牺", "步", "讓", "港", "煲", "铴", "腦", "鳝", "危", "鋪", "冠", "正",
"柽", "抍",
"掘", "控", "娴", "娀", "離", "手", "臾", "酗", "筼", "煸", "弹", "照", "哎", "毒", "颀", "诙", "刚",
"搢", "䧳",
"峒", "滋", "\\", "匀", "黉", "毓", "娠", "床", "浪", "祐", "铟", "4", "?", "凄", "飗", "蚍", "葑",
"抗", "鹞",
"糸", "红", "英", "违", "橡", "眷", "防", "缬", "龠", "察", "仍", "辇", "减", "闫", "箴", "龍", "館",
"屙", "翙",
"媽", "涴", "到", "旻", "删", "瞾", "鏖", "咭", "豨", "荘", "炭", "畼", "构", "锘", "鉫", "候", "扇",
"繄", "猩",
"瘵", "恺", "贵", "榦", "息", "恽", "胎", "狰", "雜", "辋", "璜", "硈", "泠", "呔", "蹿", "踹", "摄",
"炀", "坞",
"蹄", "裝", "赛", "蝥", "塔", "靳", "荬", "找", "仡", "淮", "比", "淆", "义", "淝", "卢", "辟", "寂",
"庒", "鳯",
"暲", "景", "邪", "腻", "赍", "甍", "讲", "哌", "嶝", "鎌", "总", "缱", "问", "磛", "谅", "拉", "靈",
"奭", "沆",
"茔", "羅", "鄠", "網", "吏", "懵", "鑫", "歌", "黹", "嵘", "涞", "碳", "崂", "婥", "赞", "镑", "購",
"幺", "鸰",
"饟", "蝌", "忝", "懒", "禺", "梽", "齉", "恳", "拯", "弥", "荡", "芾", "幪", "厌", "馒", "蜘", "欸",
"吣", "1",
"却", "榻", "碾", "袂", "錎", "钬", "無", "嬉", "笞", "蹴", "视", "雇", "创", "椟", "6", "瘁", "斜",
"傥", "喃",
"炷", "秾", "嘱", "茀", "犄", "窑", "庀", "潍", "伦", "䀲", "凉", "Р", "撻", "萜", "二", "倨", "蔑",
"捕", "勚",
"士", "鈇", "踺", "啤", "彧", "缪", "述", "傅", "颅", "畸", "畜", "滗", "慭", "琎", "斌", "参", "胳",
"骖", "稠",
"汰", "铻", "闯", "留", "蘘", "沏", "亦", "择", "華", "禽", "砟", "祼", "狃", "噫", "狼", "寤", "跪",
"浠", "·",
"费", "瓘", "鼹", "锪", "箢", "垣", "慊", "虏", "秩", "偉", "镏", "钯", "恐", "鹃", "菇", "炸", "潮",
"蟀", "硂",
"偌", "哏", "验", "桉", "阴", "初", "掴", "鹺", "峨", "赋", "舉", "裹", "赶", "土", "淋", "瘌", "沔",
"r", "赀",
"淖", "茯", "怛", "谜", "洗", "似", "舡", "纳", "晓", "R", "诐", "痹", "漪", "顺", "挛", "阎", "贝",
"钰", "惬",
"疬", "菀", "埘", "怙", "部", "译", "鲭", "窋", "敢", "夜", "撰", "珅", "特", "襕", "癖", "胡", "⒃",
"附", "擘",
"痢", "尬", "鉴", "瞋", "膨", "阽", "挲", "⒄", "骎", "帕", "缕", "计", "障", "鳆", "隹", "朔", "碹",
"当", "迦",
"氙", "蘑", "妓", "炬", "苊", "萎", "浈", "沥", "绯", "壤", "噱", "蹾", "驶", "葱", "孕", "羹", "钻",
"農", "勝",
"膈", "灿", "赆", "靿", "耱", "陪", "忙", "缰", "奶", "儒", "个", "朱", "燹", "琮", "轷", "錾", "箅",
"澳", "嗥",
"攥", "没", "匿", "鲆", "|", "矣", "他", "鸶", "芸", "B", "髑", "街", "巿", "廣", "盯", "監", "鲸",
"胭", "凬",
"寿", "挝", "绽", "+", "劝", "究", "眢", "集", "衙", "卷", "j", "跶", "牡", "畯", "貅", "销", "發",
"咱", "蓊",
"揣", "咝", "琶", "荦", "阌", "盅", "嘹", "苟", "醮", "洪", "鲧", "钒", "柱", "氨", "旰", "冽", "茭",
"嵇", "粲",
"蛾", "訾", "辔", "N", "尹", "趿", "蹲", "疟", "祠", "段", "車", "网", "⒉", "舷", "廐", "侣", "棵",
"粜", "觐",
"铼", "锁", "兒", "舁", "时", "垦", "版", "摈", "扳", "见", "腮", "嫖", "痭", "呆", "簖", "伋", "鳙",
"珊", "麂",
"既", "谴", "热", "超", "蠕", "铞", "e", "殓", "因", "锿", "文", "禊", "皙", "鑙", "爹", "鋼", "忻",
"秣", "镁",
"奠", "橉", "畺", "笮", "疹", "湝", "龟", "殃", "毵", "溃", "勢", "索", "砉", "阼", "堞", "酥", "冁",
"喊", "¥",
"幛", "娇", "锲", "蕃", "铘", "铍", "鴿", "响", "傲", "脏", "杓", "罕", "笥", "弦", "但", "缃", "扬",
"盲", "碚",
"幢", "鎖", "缺", "钋", "麽", "禳", "浃", "啄", "昧", "蒴", "帙", "琏", "咧", "舰", "亵", "浊", "豳",
"衲", "俏",
"镵", "浩", "勾", "槛", "榈", "徙", "鹤", "洹", "铂", "揎", "棕", "挦", "挫", "阆", "衹", "甚", "近",
"】", "簏",
"汽", "踮", "淌", "檇", "痔", "谝", "钙", "蕞", "蔯", "兆", "蔽", "后", "蚬", "谸", "芟", "枞", "叫",
"栗", "餘",
"营", "郝", "氯", "㺃", "狍", "冏", "庛", "纱", "泼", "碍", "认", "邓", "茵", "饧", "闟", "惝", "裙",
"噙", "忘",
"虬", "群", "S", "佗", "恼", "坟", "肮", "皮", "玃", "在", "赧", "孚", "偾", "镨", "恨", "葡", "西",
"缞", "挠",
"逃", "吾", "膪", "焦", "翘", "桧", "变", "渗", "繁", "際", "痘", "撼", "筅", "坑", "前", "玑", "数",
"融", "鲌",
"讦", "窃", "鄌", "伾", "众", "攻", "彪", "锎", "焐", "殛", "锊", "嗉", "枓", "抢", "鞠", "掩", "贾",
"搔", "皁",
"拶", "朗", "渺", "跛", "㛃", "鏾", "慥", "杆", "沈", "戍", "豫", "楠", "爆", "汤", "昉", "耘", "缡",
".", "允",
"揜", "责", "艟", "裁", "喬", "砹", "鹣", "裼", "啉", "蛳", "酮", "听", "维", "阪", "獾", "浣", "訂",
"瘿", "蜡",
"泖", "蔚", "貔", "致", "禨", "尓", "糺", "绐", "遯", "笄", "邦", "圈", "洟", "缟", "槲", "桹", "镓",
"骒", "髫",
"暾", "像", "縻", "戊", "飧", "驽", "干", "万", "绕", "披", "雅", "桊", "卡", "贲", "吡", "沧", "鳟",
"堂", "扺",
"岱", "封", "鄭", "螣", "瞩", "幞", "邕", "睫", "涩", "自", "趱", "愣", "威", "酊", "罂", "慑", "袴",
"架", "烘",
"现", "灞", "钔", "股", "興", "乍", "噜", "济", "碛", "兀", "诅", "柴", "瓿", "[", "怿", "竦", "白",
"黄", "阶",
"务", "榮", "澹", "谏", "垓", "跸", "繻", "窿", "紊", "陟", "劁", "嗑", "牯", "厉", "敛", "鮕", "嘉",
"蔻", "鼎",
"恒", "硝", "溉", "骘", "窘", "任", "裱", "处", "旨", "舶", "缸", "囹", "笠", "讥", "泜", "脊", "煊",
"淦", "牝",
"硕", "胧", "泚", "溪", "贪", "牛", "答", "瘴", "Q", "炯", "⑤", "篾", "銀", "乩", "杶", "垆", "蛐",
"苔", "啪",
"y", "玮", "琫", "寮", "邂", "後", "僵", "贴", "硭", "枚", "姆", "乎", "讶", "醭", "橥", "脱", "蒈",
"擞", "忪",
"顾", "柚", "褿", "忲", "辖", "铡", "螠", "殉", "喆", "爡", "轮", "棰", "鲉", "跃", "韬", "睡", "嘧",
"袅", "圗",
"檄", "踊", "阀", "题", "桫", "林", "沉", "禚", "散", "麇", "沦", "秋", "导", "斑", "宰", "嘞", "暑",
"笱", "搋",
"擅", "镤", "锶", "L", "厣", "有", "猗", "袆", "绞", "甭", "歧", "跣", "潦", "専", "绑", "飱", "廓",
"磔", "接",
"腓", "窎", "瑁", "飓", "蟪", "俎", "П", "缉", "䘵", "夙", "潟", "桷", "淡", "虺", "恶", "|", "驭",
"怀", "邋",
"辢", "逻", "晖", "蜃", "蜊", "溻", "冢", "尻", "礼", "厝", "亘", "酴", "饔", "悸", "戆", "什", "玚",
"馔", "哔",
"沃", "竑", "葭", "垞", "鏂", "抃", "弄", "去", "焊", "焌", "x", "苇", "與", "炼", "蛄", "莴", "阏",
"薷", "禀",
"鸯", "栽", "冒", "姓", "0", "尃", "蜞", "毗", "骟", "秸", "荸", "柈", "恬", "赡", "侏", "兑", "蝤",
"荷", "徳",
"押", "挣", "腰", "宣", "鸵", "葳", "遘", "讨", "狒", "涿", "囤", "邃", "蒜", "疑", "脍", "嘟", "鹠",
"吻", "鄹",
"耦", "华", "霸", "侥", "勒", "挞", "臊", "尺", "让", "榆", "阝", "鳚", "灾", "鲬", "艿", "Ⅱ", "锩",
"攮", "蚊",
"蔁", "唝", "涅", "挹", "淏", "鏊", "氵", "鹕", "律", "對", "粱", "恫", "挻", "滏", "叮", "‰", "鼯",
"绫", "秉",
"怩", "質", "岐", "菊", "佤", "帏", "骺", "爰", "珉", "耪", "乞", "郕", "鲱", "雷", "蒿", "不", "啐",
"侓", "郓",
"歼", "拒", "胗", "寕", "旒", "勁", "婢", "诌", "蹂", "姐", "媳", "歃", "拐", "辐", "拟", "醯", "雌",
"点", "玟",
"您", "鲨", "载", "藩", "罔", "噀", "抹", "萏", "补", "蝎", "辈", "劢", "乚", "唔", "瓜", "恿", "蟭",
"涸", "纬",
"睽", "樣", "帻", "蓥", "谯", "柯", "渴", "脁", "诲", "福", "③", "熊", "羌", "疴", "袖", "虿", "杏",
"覌", "易",
"醾", "筒", "肤", "苷", "柃", "榇", "酬", "癸", "啰", "眛", "稷", "展", "鸾", "祢", "蝾", "敵", "毛",
"痦", "老",
"赐", "单", "淹", "畋", "符", "奎", "绥", "轾", "鄺", "濩", "眦", "觎", "忾", "璋", "刘", "翳", "菪",
"簟", "胼",
"孫", "機", "獻", "低", "o", "捅", "鳥", "⑨", "卸", "废", "启", "呋", "窥", "巢", "揭", "咴", "趺",
"鲥", "空",
"膄", "崮", "锜", "络", "納", "恤", "刭", "批", "霭", "氧", "钮", "甑", "祲", "粘", "辏", "∶", "臨",
"│", "粪",
"惰", "肼", "浉", "橄", "5", "東", "8", "漁", "浜", "忍", "奂", "遹", "扃", "扦", "入", "欤", "豆",
"悠", "蕴",
"萑", "媄", "龈", "÷", "磙", "鸬", "缎", "嗯", "浕", "木", "陋", "柰", "瘩", "箨", "松", "躁", "鲇",
"彰", "恕",
"楗", "姨", "撅", "诹", "戮", "桓", "棉", "束", "嗍", "庋", "瞿", "郂", "哪", "町", "a", "铁", "洫",
"失", "栳",
"篇", "鳗", "眇", "椿", "義", "就", "都", "镌", "阖", "夭", "拃", "跚", "業", "圬", "演", "篥", "3",
"昼", "從",
"瞍", "蓦", "鼾", "坪", "觫", "鲍", "馿", "妾", "密", "奥", "耰", "佟", "嘤", "貘", "薯", "稽", "届",
"褛", "钠",
"猿", "佈", "倍", "铩", "铙", "踵", "o", "捱", "鹐", "當", "狷", "写", "遆", "钱", "姗", "寸", "综",
"挑", "礶",
"靼", "溯", "湟", "漱", "碰", "职", "味", "Λ", "璁", "壴", "给", "碘", "恭", "苴", "酚", "套", "宕",
"辽", "窀",
"催", "踢", "惟", "璧", "翔", "稿", "癀", "霈", "光", "膑", "妆", "庡", "圾", "躺", "惮", "切", "目",
"梦", "岫",
"飑", "叠", "累", "鴻", "透", "量", "妻", "沨", "俪", "屣", "挚", "w", "厅", "货", "箧", "漦", "隙",
"瘥", "妃",
"門", "鹦", "隳", "龆", "瞪", "跑", "帼", "刺", "起", "鞴", "岸", "渫", "莽", "髭", "耜", "瑛", "葉",
"宵", "绠",
"眭", "畅", "茱", "卜", "濂", "浍", "冈", "脬", "厘", "夔", "纟", "槚", "哐", "萋", "曼", "膳", "潲",
"啜", "啊",
"猾", "捉", "箕", "㐂", "藝", "艘", "園", "妲", "灵", "蚌", "范", "&", "疥", "陑", "洒", "唾", "a",
"婄", "谋",
"t", "唬", "宓", "瓷", "︰", "焗", "默", "噔", "菡", "恃", "亩", "边", "啟", "颞", "啬", "盟", "墩",
"汭", "缝",
"魇", "酡", "疗", "梾", "尾", "鹿", "锔", "溦", "酃", "囊", "掉", "罥", "报", "诤", "喹", "蕙", "割",
"蠢", "兼",
"俺", "升", "屦", "獠", "辣", "跹", "颧", "宪", "抬", "咿", "沲", "旗", "荩", "傒", "鳳", "變", "偈",
"薹", "钭",
"垧", "谪", "躐", "谀", "R", "字", "怆", "陵", "追", "迓", "舞", "卲", "捲", "麾", "税", "方", "竴",
"壹", "射",
"轫", "塞", "僧", "椠", "突", "阕", "惫", "U", "佞", "秒", "友", "視", "莹", "攒", "荻", "俅", "筐",
"能", "刓",
"拍", "浥", "揽", "谨", "军", "巷", "怵", "额", "恪", "荀", "糍", "湎", "褙", "觇", "效", "估", "噩",
"恍", "聚",
"涛", "式", "兜", "汔", "祃", "銘", "烺", "瑄", "枘", "丁", "樗", "堙", "窗", "號", "鬣", "阈", "乳",
"簇", "绳",
"淞", "]", "征", "聩", "凸", "礓", "柘", "懂", "㧎", "纷", "里", "爸", "靥", "莞", "馅", "仞", "芏",
"莳", "殪",
"煌", "落", "佚", "帱", "诶", "家", "将", "浯", "抿", "讫", "趸", "筢", "绩", "原", "专", "滂", "嗳",
"踬", "泱",
"體", "蜱", "绵", "伻", "埗", "妤", "蔺", "赳", "嘢", "梨", "鹪", "烤", "镦", "赉", "崞", "蚰", "骝",
"幅", "汴",
"丽", "访", "叩", "羼", "亓", "恸", "燥", "笤", "丑", "枰", "守", "蜚", "戈", "高", "q", "瓻", "隘",
"H", "南",
"剎", "扭", "骠", "孜", "园", "锬", "审", "¥", "罚", "购", "裰", "泔", "醌", "醒", "绎", "莪", "掬",
"睾", "鹁",
"蛊", "票", "剃", "释", "调", "黛", "撩", "篱", "茌", "敓", "蜜", "魉", "鳤", "昽", "俳", "辁", "键",
"犯", "嶙",
"狈", "邬", "枋", "婊", "憎", "督", "救", "措", "足", "码", "栟", "虼", "棻", "阅", "肊", "坛", "鸱",
"侩", "烫",
"湉", "脐", "戾", "旎", "膚", "椒", "境", "绛", "濛", "蓄", "章", "压", "窦", "彖", "阜", "咻", "神",
"存", "诺",
"課", "覺", "鲈", "渚", "「", "缢", "仓", "氅", "筇", "桴", "荥", "些", "纯", "肋", "退", "妪", "别",
"書", "租",
"嚼", "芘", "笳", "涰", "馐", "熵", "犹", "朕", "猪", "蔸", "參", "觳", "舖", "蝙", "骏", "织", "躏",
"纹", "锹",
"蛉", "撑", "氽", "茶", "俛", "誠", "胖", "崽", "炫", "乘", "把", "趑", "谵", "骧", "犍", "鸮", "灌",
"焯", "倭",
"狁", "盱", "踱", "滠", "儡", "诸", "芽", "駆", "蹼", "虱", "祇", " ", "蠡", "衄", "谬", "蒍", "婺",
"蘧", "博",
"捍", "磁", "慷", "釂", "蛩", "钕", "祎", "呑", "贫", "斗", "菅", "操", "规", "硼", "惕", "丫", "俭",
"肿", "骀",
"砼", "句", "茛", "闶", "钏", "饻", "圠", "萌", "魏", "铃", "摞", "┅", "伎", "獭", "田", "钴", "峭",
"魅", "捋",
"唼", "鹅", "祚", "嬷", "圖", "抄", "嶂", "鸳", "溧", "钵", "嗽", "墠", "锌", "愈", "併", "踟", "羯",
"翅", "纠",
"勻", "岚", "菖", "便", "祈", "毳", "屹", "掰", "倬", "扛", "巴", "拮", "绁", "跌", "飯", "嵯", "翮",
"堤", "诃",
"腑", "皆", "鄂", "胾", "片", "这", "浙", "雨", "鼱", "袼", "鹊", "厢", "蛣", "摇", "蛮", "揍", "⒅",
"啱", "薇",
"岈", "兔", "谇", "纶", "肉", "崩", "甩", "涪", "馼", "铣", "锚", "丙", "雲", "烹", "钦", "撄", "铬",
"令", "虮",
"湊", "禹", "抖", "喏", "旺", "畲", "戬", "嗷", "釜", "车", "缀", "玕", "谐", "啁", "怎", "下", "惑",
"恅", "藿",
"筰", "帐", "祸", "镝", "喵", "刁", "习", "藏", "墓", "护", "聲", "箦", "严", "按", "谛", "睨", "艚",
"歉", "蟮",
"胶", "盍", "鳃", "狯", "垫", "杠", "线", "3", "葖", "t", "熬", "虞", "嶶", "篮", "黻", "墒", "氟",
"嫠", "漕",
"腾", "哟", "玫", "撇", "垍", "靛", "翼", "淅", "省", "斥", "稞", "蠼", "谩", "埠", "蘸", "刊", "烝",
"宁", "鹚",
"龊", "苪", "袷", "诧", "细", "蒌", "焅", "2", "筹", "扒", "卮", "捞", "净", "菲", "逮", "槍", "蛎",
"莱", "黠",
"逖", "辚", "剥", "啴", "诿", "楱", "氪", "领", "嗒", "藐", "惜", "甘", "佾", "嵴", "胫", ";", "晳",
"锣", "瞅",
"缷", "耶", "搤", "策", "咽", "邀", "霾", "悟", "属", "鸪", "牴", "贞", "趁", "丞", "瘆", "豌", "著",
"饿", "筠",
"划", "璇", "损", "卵", "腒", "畏", "盥", "耐", "圏", "拓", "蒙", "鋫", "劙", "蹦", "熘", "烊", "匏",
"咔", "轘",
"沽", "菩", "罴", "磉", "炖", "假", "枪", "龙", "俸", "焱", "四", "─", "毫", "涨", "浇", "椰", "賣",
"蘇", "真",
"安", "坝", "枕", "鸼", "昵", "亨", "苤", "祷", "枧", "赟", "菑", "鳂", "戌", "悄", "種", "鳢", "嗣",
"電", "颥",
"妯", "谟", "蜒", "训", "泍", "洁", "勇", "哿", "扰", "蟆", "螂", "刃", "絜", "曪", "乒", "湖", "鞨",
"懜", "夤",
"哓", "胥", "桞", "俇", "肣", "半", "于", "橼", "锑", "熙", "甜", "槌", "盾", "屃", "缗", "共", "碗",
"凇", "笔",
"阿", "擗", "袍", "敉", "钾", "俶", "蚪", "琤", "凱", "辕", "à", "恣", "皴", "創", "寥", "妳", "腈",
"畤", "迤",
"垠", "触", "趙", "铤", "逊", "羊", "碉", "锱", "骨", "仄", "斟", "俚", "啡", "芩", "迫", "杷", "种",
"鹄", "牗",
"耑", "艇", "芰", "整", "慙", "飛", "甓", "岩", "鸦", "黎", "僊", "糁", "谈", "洋", "椑", "健", "〕",
"內", "言",
"掷", "倚", "姬", "矿", "灯", "阐", "凋", "銮", "豇", "瑰", "抓", "噪", "堋", "吁", "妈", "庥", "彳",
"鄗", "闩",
"夫", "0", "庠", "悬", "妙", "琵", "着", "首", "熣", "瞰", "揆", "燚", "条", "姜", "滘", "麓", "鳉",
"椀", "蓿",
"廠", "泌", "蝈", "倕", "丛", "耍", "且", "蝇", "凉", "豐", "泪", "臼", "服", "刍", "織", "渎", "尥",
"甙", "埋",
"珏", "援", "祖", "彊", "臱", "惦", "葴", "礁", "达", "橋", "钨", "崦", "醐", "巳", "中", "颜", "溠",
"铹", "负",
"抠", "愎", "罘", "雩", "胝", "冱", "筵", "篙", "材", "肓", "○", "迅", "腼", "橙", "仵", "茏", "慵",
"齌", "琯",
"疃", "貢", "豉", "瞟", "忉", "禄", "通", "咒", "愫", "秕", "筜", "觖", "州", "渑", "胆", "喑", "張",
"衖", "洺",
"眉", "榞", "色", "邶", "攫", "堑", "淬", "嗪", "肐", "殣", "辊", "隧", "献", "潤", "蓺", "函", "鹱",
"轭", "劭",
"椁", "膜", "亹", "侧", "貂", "哚", "磴", "蠋", "囔", "险", "伶", "世", "菥", "莶", "瘾", "燫", "延",
"∵", "毋",
"羧", "无", "雎", "曛", "沚", "巍", "g", "熄", "恰", "伷", "開", "冻", "颛", "支", "⑦", "鞥", "赖",
"试", "泮",
"联", "沮", "穰", "è", "峻", "滿", "豊", "刎", "鴈", "覆", "串", "锃", "春", "储", "矍", "哺", "评",
"猁", "愉",
"疳", "閃", "奄", "甲", "墦", "頭", "锫", "俦", "玩", "搀", "砭", "流", "橇", "泅", "琇", "趣", "∧",
"辑", "灊",
"貴", "迮", "摹", "霄", "濟", "限", "彀", "匪", "缂", "觚", "奇", "诋", "灼", "萘", "狠", "澥", "岂",
"悺", "闾",
"麋", "号", "槽", "姹", "陉", "瑯", "尉", "h", "绖", "宿", "戋", "粝", "砂", "该", "鞧", "翯", "釘",
"铢", "窨",
"設", "⒆"]`
}
File diff suppressed because one or more lines are too long
@@ -1,33 +0,0 @@
package ddddocr
import (
"embed"
_ "embed"
"likedream-goframe/internal/application/recognizer"
"os"
"path/filepath"
"strings"
)
//go:embed static/*
var fs embed.FS
func initModel() string {
if !recognizer.OnnxIsInit {
recognizer.InitOnnx()
}
// 检测文件是否存在
exePath := strings.ToLower(os.Args[0])
// 获取绝对路径
absPath, _ := filepath.Abs(exePath)
staticPath := filepath.Join(filepath.Dir(absPath), "static")
if _, err := os.Stat(staticPath); os.IsNotExist(err) {
os.Mkdir(staticPath, 0755)
}
modelPath := filepath.Join(staticPath, "common_old.onnx")
modelBytes, _ := fs.ReadFile("static/common_old.onnx")
if _, err := os.Stat(modelPath); os.IsNotExist(err) {
os.WriteFile(modelPath, modelBytes, 0644)
}
return modelPath
}
-59
View File
@@ -1,59 +0,0 @@
package recognizer
import (
"embed"
_ "embed"
"likedream-goframe/internal/framework/logger"
"os"
"path/filepath"
"runtime"
"strings"
ort "github.com/yalue/onnxruntime_go"
"go.uber.org/zap"
)
//go:embed static/*
var fs embed.FS
// OnnxIsInit 初始化onnx配置
var OnnxIsInit = false
func InitOnnx() {
name, bytes := getOnnxRuntimeName()
// 检测文件是否存在
exePath := strings.ToLower(os.Args[0])
// 获取绝对路径
absPath, _ := filepath.Abs(exePath)
filePath := filepath.Join(filepath.Dir(absPath), name)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
os.WriteFile(filePath, bytes, 0644)
}
ort.SetSharedLibraryPath(filePath)
err := ort.InitializeEnvironment()
if err != nil {
logger.Logger.Panic("初始化onnx:", zap.String("绝对路径", filePath))
return
}
OnnxIsInit = true
}
func getOnnxRuntimeName() (onnxRuntimeFileName string, onnxRuntimeFileBytes []byte) {
systemOS := runtime.GOOS
switch systemOS {
case "windows":
onnxRuntimeFileName = "onnxruntime.dll"
onnxRuntimeFileBytes, _ = fs.ReadFile("static/onnxruntime.dll")
return
case "darwin":
onnxRuntimeFileName = "onnxruntime.dylib"
onnxRuntimeFileBytes, _ = fs.ReadFile("static/libonnxruntime.1.23.2.dylib")
return
case "linux":
onnxRuntimeFileName = "onnxruntime.so"
onnxRuntimeFileBytes, _ = fs.ReadFile("static/libonnxruntime.so.1.23.2")
return
}
logger.Logger.Panic("暂不支持的操作系统:", zap.String("系统名称", systemOS))
return "", nil
}
@@ -70,3 +70,218 @@ type XueXiTCourse struct {
IsStart bool `json:"isstart"` //是否开课了,开课了为true,没开课为false。一般来说开课才能刷,不然刷不了的
State int `json:"state"` //课程状态,0为正常,1为课程已经结束
}
// XueXiTCourseChapter 课程章节信息
type XueXiTCourseChapter struct {
ID string `json:"id"` // 章节ID
Name string `json:"name"` // 章节名称
ParentNodeID string `json:"parentnodeid"` // 父节点ID
Layer int `json:"layer"` // 层级
Label string `json:"label"` // 标签
IndexOrder int `json:"indexOrder"` // 排序索引
JobCount int `json:"jobcount"` // 任务点数量
Status int `json:"status"` // 状态
IsReview int `json:"isReview"` // 是否复习
BeginTime int64 `json:"begintime"` // 开始时间
EndTime int64 `json:"endtime"` // 结束时间
Attachment []XueXiTAttachment `json:"attachment"` // 附件列表
Knowledge []XueXiTCourseChapter `json:"knowledge"` // 子章节列表
}
// XueXiTAttachment 附件信息
type XueXiTAttachment struct {
ID string `json:"id"` // 附件ID
Type string `json:"type"` // 附件类型(video/document等)
ObjectID string `json:"objectid"` // 对象ID
Extension string `json:"extension"` // 扩展信息
JobID string `json:"jobid"` // 任务ID
Property string `json:"property"` // 属性
Name string `json:"name"` // 附件名称
IsPassed bool `json:"isPassed"` // 是否已完成
}
// ChapterData 章节数据响应
type ChapterData struct {
ID string `json:"id"`
BbsID string `json:"bbsid"`
ClassScore int `json:"classscore"`
IsStart int `json:"isstart"`
AllowDownload int `json:"allowdownload"`
ChatID string `json:"chatid"`
Name string `json:"name"`
State int `json:"state"`
IsFiled int `json:"isfiled"`
VisibleScore int `json:"visiblescore"`
HideClazz int `json:"hideclazz"`
BeginDate int64 `json:"begindate"`
CourseSetting struct {
ID string `json:"id"`
CourseID string `json:"courseid"`
HiddenCourseCover int `json:"hiddencoursecover"`
CourseFaceCheck int `json:"coursefacecheck"`
} `json:"coursesetting"`
Course struct {
ID string `json:"id"`
BelongSchoolID string `json:"belongschoolid"`
Name string `json:"name"`
InfoContent string `json:"infocontent"`
ObjectID string `json:"objectid"`
App int `json:"app"`
BulletFormat string `json:"bulletformat"`
MappingCourseID string `json:"mappingcourseid"`
ImageURL string `json:"imageurl"`
TeacherFactor string `json:"teacherfactor"`
JobCount int `json:"jobcount"`
Knowledge []XueXiTCourseChapter `json:"knowledge"`
} `json:"course"`
}
// Card 任务点卡片信息
type Card struct {
ID string `json:"id"` // 卡片ID
Title string `json:"title"` // 标题
Description string `json:"description"` // 描述
KnowledgeID string `json:"knowledgeid"` // 知识点ID
Type string `json:"type"` // 类型(video/document/work等)
IsPassed bool `json:"isPassed"` // 是否完成
ObjectID string `json:"objectid"` // 对象ID
JobID string `json:"jobid"` // 任务ID
ResourceID string `json:"resourceid"` // 资源ID
Attachment *IframeAttributes `json:"attachment,omitempty"` // iframe属性
}
// IframeAttributes iframe属性
type IframeAttributes struct {
Src string `json:"src"` // iframe地址
Module string `json:"module"` // 模块名
ModToken string `json:"modtoken"` // 模块token
Width string `json:"width"` // 宽度
Height string `json:"height"` // 高度
Scrolling string `json:"scrolling"` // 滚动
ClassID string `json:"classid"` // 课程ID
ObjectID string `json:"objectid"` // 对象ID
Enc string `json:"enc"` // 加密参数
UtEnc string `json:"utenc"` // ut加密参数
OtherInfo string `json:"otherinfo"` // 其他信息
JobID string `json:"jobid"` // 任务ID
KnowledgeID string `json:"knowledgeid"` // 知识点ID
CourseFaceCheck int `json:"coursefacecheck"` // 人脸识别
}
// DataItem 任务点数据项
type DataItem struct {
ID string `json:"id"`
IndexOrder int `json:"indexorder"`
KnowledgeID string `json:"knowledgeid"`
Attachment struct {
ObjectID string `json:"objectid"`
Property struct {
Name string `json:"name"`
IsPassed bool `json:"ispassed"`
JobID string `json:"jobid"`
} `json:"property"`
Type string `json:"type"`
} `json:"attachment"`
PointList []interface{} `json:"pointList"`
JobCount int `json:"jobcount"`
Duration int64 `json:"duration"`
ClazzID string `json:"clazzid"`
CourseID string `json:"courseid"`
Module string `json:"module"`
}
// VideoDto 视频信息
type VideoDto struct {
VideoPath string `json:"videoPath"` // 视频路径
ObjectID string `json:"objectid"` // 对象ID
OtherInfo string `json:"otherInfo"` // 其他信息
JobID string `json:"jobid"` // 任务ID
Duration int `json:"duration"` // 时长(秒)
DToken string `json:"dtoken"` // dtoken
IsAudio bool `json:"isAudio"` // 是否是音频
IsPassed bool `json:"isPassed"` // 是否已完成
}
// DocumentDto 文档信息
type DocumentDto struct {
ObjectID string `json:"objectid"` // 对象ID
JobID string `json:"jobid"` // 任务ID
OtherInfo string `json:"otherInfo"` // 其他信息
JToken string `json:"jtoken"` // jtoken
PageCount int `json:"pageCount"` // 页数
IsPassed bool `json:"isPassed"` // 是否已完成
}
// FaceQRCodeResponse 人脸识别二维码响应
type FaceQRCodeResponse struct {
Status bool `json:"status"`
Msg string `json:"msg"`
QR string `json:"qr"` // 二维码base64数据
Enc string `json:"enc"` // 加密参数
}
// FaceTokenResponse 人脸识别上传令牌响应
type FaceTokenResponse struct {
Result int `json:"result"`
UploadToken string `json:"uploadtoken"`
}
// FaceUploadResponse 人脸识别上传响应
type FaceUploadResponse struct {
Result bool `json:"result"`
Msg string `json:"msg"`
Data struct {
FileID string `json:"fileId"`
} `json:"data"`
}
// FaceVerifyResponse 人脸识别验证响应
type FaceVerifyResponse struct {
Status bool `json:"status"`
Msg string `json:"msg"`
}
// WorkDto 作业信息
type WorkDto struct {
WorkID string `json:"workid"` // 作业ID
CourseID string `json:"courseid"` // 课程ID
ClassID string `json:"classid"` // 班级ID
KnowledgeID string `json:"knowledgeid"` // 知识点ID
JobID string `json:"jobid"` // 任务ID
Enc string `json:"enc"` // 加密参数
UTEnc string `json:"utenc"` // UT加密参数
Title string `json:"title"` // 作业标题
IsPassed bool `json:"isPassed"` // 是否已完成
Questions []WorkQuestion `json:"questions"` // 题目列表
}
// WorkQuestion 作业题目
type WorkQuestion struct {
QuestionID string `json:"questionId"` // 题目ID
Type int `json:"type"` // 题目类型: 0单选 1多选 2判断 3填空 4简答
Title string `json:"title"` // 题目标题
Description string `json:"description"` // 题目描述
Options []string `json:"options"` // 选项列表(单选/多选/判断)
Answer string `json:"answer"` // 答案
Score float64 `json:"score"` // 分值
}
// WorkSubmitRequest 作业提交请求
type WorkSubmitRequest struct {
WorkID string `json:"workid"`
TotalScore string `json:"totalScore"`
AnswerList []WorkAnswerItem `json:"answerlist"`
UserId string `json:"userId"`
ClassId string `json:"classId"`
CourseId string `json:"courseId"`
KnowledgeId string `json:"knowledgeid"`
JobId string `json:"jobid"`
Enc string `json:"enc"`
UTEnc string `json:"utenc"`
}
// WorkAnswerItem 作业答案项
type WorkAnswerItem struct {
QuestionID string `json:"questionId"`
Answer string `json:"answer"`
}
@@ -24,10 +24,12 @@ type XuexitongLogin struct {
func NewXuexitongLogin(ctx context.Context) *XuexitongLogin {
return &XuexitongLogin{
client: httpclient.Xuexitong.Clone(ctx),
client: httpclient.NewXuexitongClient(),
}
}
func (c *XuexitongLogin) Login(ctx context.Context, username, password string, retry int) (err error) {
jar := cookiejar.NewAllSavePersistCookieJar()
c.client.SetCookieJar(jar)
usernameEncrypted, err := cryptoutil.LoginEncrypt(username)
if err != nil {
logger.Logger.Error("LoginEncrypt err:", zap.Error(err))
@@ -3,10 +3,6 @@ package xuexitong
import (
"fmt"
"io"
"likedream-goframe/internal/utils"
"log"
"math/rand"
"os"
"strings"
"time"
@@ -15,10 +11,8 @@ import (
var randChar []string = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "A", "B", "C", "D", "E", "F"}
func ApiVerifyCode(client *resty.Client, retry int) (string, error) {
if retry < 0 {
return "", fmt.Errorf("retry limit exceeded")
}
func ApiVerifyCodeBytes(client *resty.Client) ([]byte, error) {
urlStr := "https://mooc1-api.chaoxing.com/processVerifyPng.ac?t=" + fmt.Sprintf("%d", time.Now().UnixMilli())
req := client.R()
@@ -28,40 +22,14 @@ func ApiVerifyCode(client *resty.Client, retry int) (string, error) {
req.Header.Add("Connection", "keep-alive")
res, err := req.Get(urlStr)
if err != nil {
if retry > 0 {
return ApiVerifyCode(client, retry-1)
} else {
return res.String(), err
}
return nil, err
}
codeFileName := "code" + randChar[rand.Intn(len(randChar))] //生成验证码文件名称
for i := 0; i < 10; i++ {
codeFileName += randChar[rand.Intn(len(randChar))]
}
codeFileName += ".png"
utils.PathExistForCreate("./assets/code/") //检测是否存在路径,如果不存在则创建
filepath := fmt.Sprintf("./assets/code/%s", codeFileName)
file, err := os.Create(filepath)
defer res.Body.Close()
all, err := io.ReadAll(res.Body)
if err != nil {
res.Body.Close() //立即释放
log.Println(err)
return "", err
return nil, err
}
_, err = io.Copy(file, res.Body)
if err != nil {
res.Body.Close() //立即释放
log.Println(err)
return "", err
}
file.Close()
if utils.IsBadImg(filepath) {
res.Body.Close() //立即释放
utils.DeleteFile(filepath) //删除坏的文件
return ApiVerifyCode(client, retry-1)
}
return filepath, nil
return all, nil
}
func ApiPassVerifyCode(client *resty.Client, code string, retry int) bool {
File diff suppressed because it is too large Load Diff
@@ -1,18 +0,0 @@
package xuexitong
import (
"context"
"likedream-goframe/internal/utils"
"testing"
)
func TestXuexitongLogin(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
list, err := courseAction.PullCourseList(context.Background(), 5)
utils.OkOrPanic(err)
t.Log(list)
}
@@ -0,0 +1,253 @@
package xuexitong
import (
"context"
"likedream-goframe/internal/application/xuexitong/entity"
"likedream-goframe/internal/utils"
"testing"
)
func TestXuexitongLogin(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
}
func TestPullCourse(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
list := courseAction.PullCourseList(context.Background())
utils.OkOrPanic(err)
t.Log(list)
}
func TestPullChapter(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
// 先获取课程列表
courseList := courseAction.PullCourseList(context.Background())
if len(courseList) == 0 {
t.Fatal("没有找到课程")
}
// 获取第一个课程的章节
chapters := courseAction.PullCourseChapterList(context.Background(), courseList[0].CourseID)
t.Logf("课程 %s 的章节数量: %d", courseList[0].CourseName, len(chapters))
for _, chapter := range chapters {
t.Logf("章节: %s, 任务点数量: %d", chapter.Name, chapter.JobCount)
}
}
func TestFetchChapterCards(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
// 获取课程列表
courseList := courseAction.PullCourseList(context.Background())
if len(courseList) == 0 {
t.Fatal("没有找到课程")
}
course := courseList[0]
// 获取章节列表
chapters := courseAction.PullCourseChapterList(context.Background(), course.CourseID)
if len(chapters) == 0 {
t.Fatal("没有找到章节")
}
// 找一个有任务点的章节
var targetChapter *entity.XueXiTCourseChapter
for i := range chapters {
if chapters[i].JobCount > 0 {
targetChapter = &chapters[i]
break
}
}
if targetChapter == nil {
t.Fatal("没有找到有任务点的章节")
}
// 获取任务点卡片
cards, err := courseAction.FetchChapterCards(context.Background(), course, targetChapter.ID, targetChapter.ID)
utils.OkOrPanic(err)
t.Logf("章节 %s 的任务点数量: %d", targetChapter.Name, len(cards))
for _, card := range cards {
t.Logf("任务点: 类型=%s, JobID=%s, ObjectID=%s", card.Type, card.JobID, card.ObjectID)
}
}
func TestAutoPlayVideo(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
// 获取课程列表
courseList := courseAction.PullCourseList(context.Background())
if len(courseList) == 0 {
t.Fatal("没有找到课程")
}
course := courseList[0]
// 获取章节列表
chapters := courseAction.PullCourseChapterList(context.Background(), course.CourseID)
if len(chapters) == 0 {
t.Fatal("没有找到章节")
}
// 找一个有任务点的章节
var targetChapter *entity.XueXiTCourseChapter
for i := range chapters {
if chapters[i].JobCount > 0 {
targetChapter = &chapters[i]
break
}
}
if targetChapter == nil {
t.Fatal("没有找到有任务点的章节")
}
// 获取任务点卡片
cards, err := courseAction.FetchChapterCards(context.Background(), course, targetChapter.ID, targetChapter.ID)
utils.OkOrPanic(err)
// 找视频任务点
for _, card := range cards {
if card.Type == "video" {
t.Logf("开始处理视频任务点: %s", card.JobID)
// 获取视频信息
videoDto, err := courseAction.FetchVideoDto(context.Background(), card.ObjectID, "", card.JobID)
utils.OkOrPanic(err)
// 自动播放视频
err = courseAction.AutoPlayVideo(context.Background(), course, card, videoDto)
utils.OkOrPanic(err)
t.Logf("视频任务点完成: %s", card.JobID)
break // 只测试一个视频
}
}
}
func TestAutoReadDocument(t *testing.T) {
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
client := xuexitongLogin.GetClient()
courseAction := NewXuexitongCourse(client)
// 获取课程列表
courseList := courseAction.PullCourseList(context.Background())
if len(courseList) == 0 {
t.Fatal("没有找到课程")
}
course := courseList[0]
// 获取章节列表
chapters := courseAction.PullCourseChapterList(context.Background(), course.CourseID)
if len(chapters) == 0 {
t.Fatal("没有找到章节")
}
// 找一个有任务点的章节
var targetChapter *entity.XueXiTCourseChapter
for i := range chapters {
if chapters[i].JobCount > 0 {
targetChapter = &chapters[i]
break
}
}
if targetChapter == nil {
t.Fatal("没有找到有任务点的章节")
}
// 获取任务点卡片
cards, err := courseAction.FetchChapterCards(context.Background(), course, targetChapter.ID, targetChapter.ID)
utils.OkOrPanic(err)
// 找文档任务点
for _, card := range cards {
if card.Type == "document" {
t.Logf("开始处理文档任务点: %s", card.JobID)
// 获取文档信息
documentDto, err := courseAction.FetchDocumentDto(context.Background(), card.ObjectID, "", card.JobID)
utils.OkOrPanic(err)
// 自动阅读文档
err = courseAction.AutoReadDocument(context.Background(), course, card, documentDto)
utils.OkOrPanic(err)
t.Logf("文档任务点完成: %s", card.JobID)
break // 只测试一个文档
}
}
}
// TestAutoStudyByCourseID 测试通过课程ID刷课(定时任务使用)
func TestAutoStudyByCourseID(t *testing.T) {
// 登录
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
// 创建课程实例
courseAction := NewXuexitongCourse(xuexitongLogin.GetClient())
// 先获取课程列表,拿到第一个课程的ID
courseList := courseAction.PullCourseList(context.Background())
if len(courseList) == 0 {
t.Fatal("没有找到课程")
}
courseID := courseList[0].CourseID
t.Logf("准备刷课程ID: %s", courseID)
// 一键刷指定课程的所有任务(定时任务就调用这一个方法)
err = courseAction.AutoStudyByCourseID(context.Background(), courseID)
utils.OkOrPanic(err)
t.Log("指定课程刷课完成")
}
// TestAutoStudyAllCourses 测试自动刷所有课程
func TestAutoStudyAllCourses(t *testing.T) {
// 登录
xuexitongLogin := NewXuexitongLogin(context.Background())
err := xuexitongLogin.Login(context.Background(), "13551503012", "xiaozhi520", 5)
utils.OkOrPanic(err)
// 创建课程实例
courseAction := NewXuexitongCourse(xuexitongLogin.GetClient())
// 一键刷所有课程
err = courseAction.AutoStudyAllCourses(context.Background())
utils.OkOrPanic(err)
t.Log("所有课程刷课完成")
}
+10 -6
View File
@@ -8,13 +8,13 @@ const (
ConfigDatabaseType = "database.type"
ConfigDatabaseDsn = "database.dsn"
ConfigDatabaseDsn2 = "database.dsn2"
ConfigServerPort = "server.port"
ConfigEmailConfirmBaseUrl = "server.emailConfirmBaseUrl"
ConfigEmailResetPasswordUrl = "server.emailResetPasswordUrl"
ConfigBalanceMigrateUrl = "server.balanceMigrateUrl"
ConfigAuthorizationKey = "server.authorization"
ConfigServerPort = "server.port"
ConfigEmailConfirmBaseUrl = "server.emailConfirmBaseUrl"
ConfigEmailResetPasswordUrl = "server.emailResetPasswordUrl"
ConfigBalanceMigrateUrl = "server.balanceMigrateUrl"
ConfigAuthorizationKey = "server.authorization"
ConfigJwtKey = "server.jwt"
ConfigRuoliCaptchaBaseUrl = "ruoliCaptcha.baseUrl"
ConfigCaptchaBaseUrl = "captcha.baseUrl"
ConfigS3AccessKey = "s3.accessKey"
ConfigS3SecretKey = "s3.secretKey"
ConfigS3BaseUrl = "s3.baseUrl"
@@ -22,6 +22,7 @@ const (
ConfigCpdailyAppVersion = "cpdaily.appVersion"
ConfigCpdailySystemVersion = "cpdaily.systemVersion"
ConfigCpdailyCalVersion = "cpdaily.calVersion"
ConfigSliderSchoolIds = "cpdaily.sliderIds"
ConfigRedisUrl = "redis.url"
@@ -34,6 +35,9 @@ const (
ConfigEmailSMTPPassword = "email.smtpPassword"
ConfigEmailFromEmail = "email.fromEmail"
ConfigEmailFromName = "email.fromName"
// 基础积分配置
ConfigBasePointCpdaily = "basePoint.cpdaily"
)
const (
+2
View File
@@ -7,6 +7,7 @@ const (
RedisBalanceMigrate = "balancemigrate:" // 积分转移token
RedisCpUserLogin = "cpuserlogin:"
RedisCpUserAuthorize = "cpuser:authorize:"
RedisSchoolInfo = "cpuser:schoolinfo:"
)
var (
@@ -16,4 +17,5 @@ var (
RedisBalanceMigrateExpire = int64(3600) // 积分转移链接1小时有效
RedisCpUserLoginExpire = int64(180)
RedisCpUserAuthorizeExpire = int64(1800)
RedisSchoolInfoExpire = int64(3600) // 学校信息1小时有效
)
+1 -1
View File
@@ -27,7 +27,7 @@ func InitDB() {
client, err := ent.Open(dbType.String(), dbDsn.String())
utils.OkOrPanic(err)
// 自动迁移
if err := client.Debug().Schema.Create(context.Background()); err != nil {
if err := client.Schema.Create(context.Background()); err != nil {
logger.Logger.Fatal("failed creating schema resources: %v", zap.Error(err))
}
Global = client
+19
View File
@@ -0,0 +1,19 @@
package course
import (
"context"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/xuexitong/entity"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/utils"
)
type IXxtCtrlV1 interface {
List(ctx context.Context, req *v1.CourseListReq) (*utils.Response[[]*ent.CourseEntity], error)
AddCourse(ctx context.Context, req *v1.AddCourseReq) (*utils.Response[any], error)
UpdatePassword(ctx context.Context, req *v1.UpdateCoursePasswordReq) (*utils.Response[any], error)
RestartCourse(ctx context.Context, req *v1.RestartCourseReq) (*utils.Response[any], error)
PauseCourse(ctx context.Context, req *v1.PauseCourseReq) (*utils.Response[any], error)
DeleteCourse(ctx context.Context, req *v1.DeleteCourseReq) (*utils.Response[any], error)
QueryCourses(ctx context.Context, req *v1.QueryCourseReq) (*utils.Response[[]entity.XueXiTCourse], error)
}
+59
View File
@@ -0,0 +1,59 @@
package course
import (
"context"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/xuexitong/entity"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/service"
"likedream-goframe/internal/utils"
)
type XxtCtrlV1 struct {
}
// List 获取刷课列表
func (c XxtCtrlV1) List(ctx context.Context, req *v1.CourseListReq) (res *utils.Response[[]*ent.CourseEntity], err error) {
defer utils.Pass(&err)
return service.Global.Course.List(ctx, req), nil
}
// AddCourse 添加刷课
func (c XxtCtrlV1) AddCourse(ctx context.Context, req *v1.AddCourseReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.Course.AddCourse(ctx, req), nil
}
// UpdatePassword 修改刷课密码
func (c XxtCtrlV1) UpdatePassword(ctx context.Context, req *v1.UpdateCoursePasswordReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.Course.UpdatePassword(ctx, req), nil
}
// RestartCourse 重刷课程
func (c XxtCtrlV1) RestartCourse(ctx context.Context, req *v1.RestartCourseReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.Course.RestartCourse(ctx, req), nil
}
// PauseCourse 暂停刷课
func (c XxtCtrlV1) PauseCourse(ctx context.Context, req *v1.PauseCourseReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.Course.PauseCourse(ctx, req), nil
}
// DeleteCourse 删除刷课
func (c XxtCtrlV1) DeleteCourse(ctx context.Context, req *v1.DeleteCourseReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.Course.DeleteCourse(ctx, req), nil
}
// QueryCourses 查询课程列表
func (c XxtCtrlV1) QueryCourses(ctx context.Context, req *v1.QueryCourseReq) (res *utils.Response[[]entity.XueXiTCourse], err error) {
defer utils.Pass(&err)
return service.Global.Course.QueryCourses(ctx, req), nil
}
func NewXxtCtrlV1() IXxtCtrlV1 {
return &XxtCtrlV1{}
}
@@ -3,6 +3,7 @@ package cpdaily
import (
"context"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/cpdaily/map_info"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/service"
"likedream-goframe/internal/utils"
@@ -11,6 +12,11 @@ import (
type CpTaskCtrlV1 struct {
}
func (c CpTaskCtrlV1) FindLocation(ctx context.Context, req *v1.FindLocationReq) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return utils.SuccessWithData[any](map_info.SearchAddressLongitudeAndLatitude(ctx, req.Address)), nil
}
func (c CpTaskCtrlV1) List(ctx context.Context, req *v1.CpTaskListReq) (res *utils.Response[[]*ent.CpTask], err error) {
defer utils.Pass(&err)
return service.Global.CpTask.List(ctx, req), nil
@@ -59,6 +65,12 @@ func (c CpTaskCtrlV1) PauseTask(ctx context.Context, req *v1.PauseTaskReq) (res
defer utils.Pass(&err)
return service.Global.CpTask.PauseTask(ctx, req), nil
}
func (c CpTaskCtrlV1) GetTaskStatistics(ctx context.Context, req *v1.GetTaskStatisticsReq) (res *utils.Response[[]*v1.TaskStatisticsItem], err error) {
defer utils.Pass(&err)
return service.Global.CpTask.GetTaskStatistics(ctx, req), nil
}
func NewCpTaskCtrlV1() ICpTaskCtrlV1 {
return &CpTaskCtrlV1{}
}
@@ -2,9 +2,7 @@ package cpdaily
import (
"context"
"io"
v1 "likedream-goframe/internal/api/v1"
"likedream-goframe/internal/application/recognizer/classifier"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/service"
"likedream-goframe/internal/utils"
@@ -13,14 +11,6 @@ import (
type CpTaskLogCtrlV1 struct {
}
func (c CpTaskLogCtrlV1) OCR(ctx context.Context, req *v1.CpTaskOCRReq) (res *utils.Response[any], err error) {
file, _ := req.File.Open()
defer file.Close()
bytes, _ := io.ReadAll(file)
result := classifier.ClassifyImage(bytes, req.Name)
return utils.SuccessWithData[any](result), nil
}
func (c CpTaskLogCtrlV1) List(ctx context.Context, req *v1.CpTaskLogListReq) (res *utils.Response[[]*ent.CpTaskLog], err error) {
defer utils.Pass(&err)
return service.Global.CpTaskLog.List(ctx, req), nil
@@ -61,3 +61,12 @@ func (c CpUserCtrlV1) AuthorizeRandom(ctx context.Context, req *v1.CpUserAuthori
defer utils.Pass(&err)
return service.Global.CpUser.AuthorizeRandom(ctx, req), nil
}
func (c CpUserCtrlV1) LoginGetCode(ctx context.Context, req *v1.LoginGetCode) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.CpUser.LoginGetCode(ctx, req), nil
}
func (c CpUserCtrlV1) AddByCode(ctx context.Context, req *v1.AddByCode) (res *utils.Response[any], err error) {
defer utils.Pass(&err)
return service.Global.CpUser.AddByCode(ctx, req), nil
}
@@ -18,4 +18,6 @@ type ICpTaskCtrlV1 interface {
Delete(ctx context.Context, req *v1.DeleteTaskReq) (res *utils.Response[any], err error)
ExecuteNow(ctx context.Context, req *v1.ExecuteNowReq) (res *utils.Response[any], err error)
PauseTask(ctx context.Context, req *v1.PauseTaskReq) (res *utils.Response[any], err error)
FindLocation(ctx context.Context, req *v1.FindLocationReq) (res *utils.Response[any], err error)
GetTaskStatistics(ctx context.Context, req *v1.GetTaskStatisticsReq) (res *utils.Response[[]*v1.TaskStatisticsItem], err error)
}
@@ -9,6 +9,4 @@ import (
type ICpTaskLogCtrlV1 interface {
List(ctx context.Context, req *v1.CpTaskLogListReq) (res *utils.Response[[]*ent.CpTaskLog], err error)
OCR(ctx context.Context, req *v1.CpTaskOCRReq) (res *utils.Response[any], err error)
}
@@ -25,4 +25,8 @@ type ICpUserCtrlV1 interface {
AuthorizeConfirm(ctx context.Context, req *v1.CpUserAuthorizeConfirmReq) (*utils.Response[any], error)
// 随机授权
AuthorizeRandom(ctx context.Context, req *v1.CpUserAuthorizeRandomReq) (res *utils.Response[any], err error)
// 手机登录获取验证码
LoginGetCode(ctx context.Context, req *v1.LoginGetCode) (*utils.Response[any], error)
AddByCode(ctx context.Context, req *v1.AddByCode) (*utils.Response[any], error)
}
+215 -35
View File
@@ -11,6 +11,7 @@ import (
"likedream-goframe/internal/ent/migrate"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/cpimage"
"likedream-goframe/internal/ent/cpschool"
"likedream-goframe/internal/ent/cptask"
@@ -37,6 +38,8 @@ type Client struct {
config
// Schema is the client for creating, migrating and dropping schema.
Schema *migrate.Schema
// CourseEntity is the client for interacting with the CourseEntity builders.
CourseEntity *CourseEntityClient
// CpImage is the client for interacting with the CpImage builders.
CpImage *CpImageClient
// CpSchool is the client for interacting with the CpSchool builders.
@@ -70,6 +73,7 @@ func NewClient(opts ...Option) *Client {
func (c *Client) init() {
c.Schema = migrate.NewSchema(c.driver)
c.CourseEntity = NewCourseEntityClient(c.config)
c.CpImage = NewCpImageClient(c.config)
c.CpSchool = NewCpSchoolClient(c.config)
c.CpTask = NewCpTaskClient(c.config)
@@ -173,19 +177,20 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
cfg := c.config
cfg.driver = tx
return &Tx{
ctx: ctx,
config: cfg,
CpImage: NewCpImageClient(cfg),
CpSchool: NewCpSchoolClient(cfg),
CpTask: NewCpTaskClient(cfg),
CpTaskLog: NewCpTaskLogClient(cfg),
CpUser: NewCpUserClient(cfg),
SysAgent: NewSysAgentClient(cfg),
SysCard: NewSysCardClient(cfg),
SysMenu: NewSysMenuClient(cfg),
SysRole: NewSysRoleClient(cfg),
SysUser: NewSysUserClient(cfg),
SysUserLog: NewSysUserLogClient(cfg),
ctx: ctx,
config: cfg,
CourseEntity: NewCourseEntityClient(cfg),
CpImage: NewCpImageClient(cfg),
CpSchool: NewCpSchoolClient(cfg),
CpTask: NewCpTaskClient(cfg),
CpTaskLog: NewCpTaskLogClient(cfg),
CpUser: NewCpUserClient(cfg),
SysAgent: NewSysAgentClient(cfg),
SysCard: NewSysCardClient(cfg),
SysMenu: NewSysMenuClient(cfg),
SysRole: NewSysRoleClient(cfg),
SysUser: NewSysUserClient(cfg),
SysUserLog: NewSysUserLogClient(cfg),
}, nil
}
@@ -203,26 +208,27 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{
ctx: ctx,
config: cfg,
CpImage: NewCpImageClient(cfg),
CpSchool: NewCpSchoolClient(cfg),
CpTask: NewCpTaskClient(cfg),
CpTaskLog: NewCpTaskLogClient(cfg),
CpUser: NewCpUserClient(cfg),
SysAgent: NewSysAgentClient(cfg),
SysCard: NewSysCardClient(cfg),
SysMenu: NewSysMenuClient(cfg),
SysRole: NewSysRoleClient(cfg),
SysUser: NewSysUserClient(cfg),
SysUserLog: NewSysUserLogClient(cfg),
ctx: ctx,
config: cfg,
CourseEntity: NewCourseEntityClient(cfg),
CpImage: NewCpImageClient(cfg),
CpSchool: NewCpSchoolClient(cfg),
CpTask: NewCpTaskClient(cfg),
CpTaskLog: NewCpTaskLogClient(cfg),
CpUser: NewCpUserClient(cfg),
SysAgent: NewSysAgentClient(cfg),
SysCard: NewSysCardClient(cfg),
SysMenu: NewSysMenuClient(cfg),
SysRole: NewSysRoleClient(cfg),
SysUser: NewSysUserClient(cfg),
SysUserLog: NewSysUserLogClient(cfg),
}, nil
}
// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
//
// client.Debug().
// CpImage.
// CourseEntity.
// Query().
// Count(ctx)
func (c *Client) Debug() *Client {
@@ -245,8 +251,8 @@ func (c *Client) Close() error {
// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{
c.CpImage, c.CpSchool, c.CpTask, c.CpTaskLog, c.CpUser, c.SysAgent, c.SysCard,
c.SysMenu, c.SysRole, c.SysUser, c.SysUserLog,
c.CourseEntity, c.CpImage, c.CpSchool, c.CpTask, c.CpTaskLog, c.CpUser,
c.SysAgent, c.SysCard, c.SysMenu, c.SysRole, c.SysUser, c.SysUserLog,
} {
n.Use(hooks...)
}
@@ -256,8 +262,8 @@ func (c *Client) Use(hooks ...Hook) {
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{
c.CpImage, c.CpSchool, c.CpTask, c.CpTaskLog, c.CpUser, c.SysAgent, c.SysCard,
c.SysMenu, c.SysRole, c.SysUser, c.SysUserLog,
c.CourseEntity, c.CpImage, c.CpSchool, c.CpTask, c.CpTaskLog, c.CpUser,
c.SysAgent, c.SysCard, c.SysMenu, c.SysRole, c.SysUser, c.SysUserLog,
} {
n.Intercept(interceptors...)
}
@@ -266,6 +272,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
// Mutate implements the ent.Mutator interface.
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
switch m := m.(type) {
case *CourseEntityMutation:
return c.CourseEntity.mutate(ctx, m)
case *CpImageMutation:
return c.CpImage.mutate(ctx, m)
case *CpSchoolMutation:
@@ -293,6 +301,159 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
}
}
// CourseEntityClient is a client for the CourseEntity schema.
type CourseEntityClient struct {
config
}
// NewCourseEntityClient returns a client for the CourseEntity from the given config.
func NewCourseEntityClient(c config) *CourseEntityClient {
return &CourseEntityClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `courseentity.Hooks(f(g(h())))`.
func (c *CourseEntityClient) Use(hooks ...Hook) {
c.hooks.CourseEntity = append(c.hooks.CourseEntity, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `courseentity.Intercept(f(g(h())))`.
func (c *CourseEntityClient) Intercept(interceptors ...Interceptor) {
c.inters.CourseEntity = append(c.inters.CourseEntity, interceptors...)
}
// Create returns a builder for creating a CourseEntity entity.
func (c *CourseEntityClient) Create() *CourseEntityCreate {
mutation := newCourseEntityMutation(c.config, OpCreate)
return &CourseEntityCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of CourseEntity entities.
func (c *CourseEntityClient) CreateBulk(builders ...*CourseEntityCreate) *CourseEntityCreateBulk {
return &CourseEntityCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *CourseEntityClient) MapCreateBulk(slice any, setFunc func(*CourseEntityCreate, int)) *CourseEntityCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &CourseEntityCreateBulk{err: fmt.Errorf("calling to CourseEntityClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*CourseEntityCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &CourseEntityCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for CourseEntity.
func (c *CourseEntityClient) Update() *CourseEntityUpdate {
mutation := newCourseEntityMutation(c.config, OpUpdate)
return &CourseEntityUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *CourseEntityClient) UpdateOne(_m *CourseEntity) *CourseEntityUpdateOne {
mutation := newCourseEntityMutation(c.config, OpUpdateOne, withCourseEntity(_m))
return &CourseEntityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *CourseEntityClient) UpdateOneID(id int64) *CourseEntityUpdateOne {
mutation := newCourseEntityMutation(c.config, OpUpdateOne, withCourseEntityID(id))
return &CourseEntityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for CourseEntity.
func (c *CourseEntityClient) Delete() *CourseEntityDelete {
mutation := newCourseEntityMutation(c.config, OpDelete)
return &CourseEntityDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *CourseEntityClient) DeleteOne(_m *CourseEntity) *CourseEntityDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *CourseEntityClient) DeleteOneID(id int64) *CourseEntityDeleteOne {
builder := c.Delete().Where(courseentity.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &CourseEntityDeleteOne{builder}
}
// Query returns a query builder for CourseEntity.
func (c *CourseEntityClient) Query() *CourseEntityQuery {
return &CourseEntityQuery{
config: c.config,
ctx: &QueryContext{Type: TypeCourseEntity},
inters: c.Interceptors(),
}
}
// Get returns a CourseEntity entity by its id.
func (c *CourseEntityClient) Get(ctx context.Context, id int64) (*CourseEntity, error) {
return c.Query().Where(courseentity.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *CourseEntityClient) GetX(ctx context.Context, id int64) *CourseEntity {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QuerySysUser queries the sys_user edge of a CourseEntity.
func (c *CourseEntityClient) QuerySysUser(_m *CourseEntity) *SysUserQuery {
query := (&SysUserClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(courseentity.Table, courseentity.FieldID, id),
sqlgraph.To(sysuser.Table, sysuser.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, courseentity.SysUserTable, courseentity.SysUserColumn),
)
schemaConfig := _m.schemaConfig
step.To.Schema = schemaConfig.SysUser
step.Edge.Schema = schemaConfig.CourseEntity
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *CourseEntityClient) Hooks() []Hook {
hooks := c.hooks.CourseEntity
return append(hooks[:len(hooks):len(hooks)], courseentity.Hooks[:]...)
}
// Interceptors returns the client interceptors.
func (c *CourseEntityClient) Interceptors() []Interceptor {
return c.inters.CourseEntity
}
func (c *CourseEntityClient) mutate(ctx context.Context, m *CourseEntityMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&CourseEntityCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&CourseEntityUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&CourseEntityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&CourseEntityDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown CourseEntity mutation op: %q", m.Op())
}
}
// CpImageClient is a client for the CpImage schema.
type CpImageClient struct {
config
@@ -2139,6 +2300,25 @@ func (c *SysUserClient) QueryCpTask(_m *SysUser) *CpTaskQuery {
return query
}
// QueryCourseEntity queries the course_entity edge of a SysUser.
func (c *SysUserClient) QueryCourseEntity(_m *SysUser) *CourseEntityQuery {
query := (&CourseEntityClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(sysuser.Table, sysuser.FieldID, id),
sqlgraph.To(courseentity.Table, courseentity.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, sysuser.CourseEntityTable, sysuser.CourseEntityColumn),
)
schemaConfig := _m.schemaConfig
step.To.Schema = schemaConfig.CourseEntity
step.Edge.Schema = schemaConfig.CourseEntity
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryCpTaskLogs queries the cp_task_logs edge of a SysUser.
func (c *SysUserClient) QueryCpTaskLogs(_m *SysUser) *CpTaskLogQuery {
query := (&CpTaskLogClient{config: c.config}).Query()
@@ -2377,12 +2557,12 @@ func (c *SysUserLogClient) mutate(ctx context.Context, m *SysUserLogMutation) (V
// hooks and interceptors per client, for fast access.
type (
hooks struct {
CpImage, CpSchool, CpTask, CpTaskLog, CpUser, SysAgent, SysCard, SysMenu,
SysRole, SysUser, SysUserLog []ent.Hook
CourseEntity, CpImage, CpSchool, CpTask, CpTaskLog, CpUser, SysAgent, SysCard,
SysMenu, SysRole, SysUser, SysUserLog []ent.Hook
}
inters struct {
CpImage, CpSchool, CpTask, CpTaskLog, CpUser, SysAgent, SysCard, SysMenu,
SysRole, SysUser, SysUserLog []ent.Interceptor
CourseEntity, CpImage, CpSchool, CpTask, CpTaskLog, CpUser, SysAgent, SysCard,
SysMenu, SysRole, SysUser, SysUserLog []ent.Interceptor
}
)
+288
View File
@@ -0,0 +1,288 @@
// ENT数据库文件,请勿编辑。
package ent
import (
"fmt"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/sysuser"
"likedream-goframe/internal/framework/ent2"
"strings"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
)
// CourseEntity is the model entity for the CourseEntity schema.
type CourseEntity struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// CreateTime holds the value of the "create_time" field.
CreateTime *ent2.CustomTime `json:"createTime"`
// UpdateTime holds the value of the "update_time" field.
UpdateTime *ent2.CustomTime `json:"updateTime"`
// UserID holds the value of the "user_id" field.
UserID int64 `json:"user_id,omitempty"`
// Username holds the value of the "username" field.
Username string `json:"username,omitempty"`
// Password holds the value of the "password" field.
Password string `json:"-"`
// Type holds the value of the "type" field.
Type courseentity.Type `json:"type,omitempty"`
// CourseName holds the value of the "course_name" field.
CourseName string `json:"course_name,omitempty"`
// CourseID holds the value of the "course_id" field.
CourseID string `json:"course_id,omitempty"`
// Need holds the value of the "need" field.
Need courseentity.Need `json:"need,omitempty"`
// Status holds the value of the "status" field.
Status courseentity.Status `json:"status,omitempty"`
// TaskCount holds the value of the "task_count" field.
TaskCount int `json:"task_count,omitempty"`
// SuccessCount holds the value of the "success_count" field.
SuccessCount int `json:"success_count,omitempty"`
// ExamState holds the value of the "exam_state" field.
ExamState courseentity.ExamState `json:"exam_state,omitempty"`
// Violence holds the value of the "violence" field.
Violence courseentity.Violence `json:"violence,omitempty"`
// Desc holds the value of the "desc" field.
Desc string `json:"desc,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the CourseEntityQuery when eager-loading is set.
Edges CourseEntityEdges `json:"edges"`
selectValues sql.SelectValues
}
// CourseEntityEdges holds the relations/edges for other nodes in the graph.
type CourseEntityEdges struct {
// SysUser holds the value of the sys_user edge.
SysUser *SysUser `json:"sys_user,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// SysUserOrErr returns the SysUser value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e CourseEntityEdges) SysUserOrErr() (*SysUser, error) {
if e.SysUser != nil {
return e.SysUser, nil
} else if e.loadedTypes[0] {
return nil, &NotFoundError{label: sysuser.Label}
}
return nil, &NotLoadedError{edge: "sys_user"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*CourseEntity) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case courseentity.FieldCreateTime, courseentity.FieldUpdateTime:
values[i] = new(ent2.CustomTime)
case courseentity.FieldID, courseentity.FieldUserID, courseentity.FieldTaskCount, courseentity.FieldSuccessCount:
values[i] = new(sql.NullInt64)
case courseentity.FieldUsername, courseentity.FieldPassword, courseentity.FieldType, courseentity.FieldCourseName, courseentity.FieldCourseID, courseentity.FieldNeed, courseentity.FieldStatus, courseentity.FieldExamState, courseentity.FieldViolence, courseentity.FieldDesc:
values[i] = new(sql.NullString)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the CourseEntity fields.
func (_m *CourseEntity) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case courseentity.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case courseentity.FieldCreateTime:
if value, ok := values[i].(*ent2.CustomTime); !ok {
return fmt.Errorf("unexpected type %T for field create_time", values[i])
} else if value != nil {
_m.CreateTime = value
}
case courseentity.FieldUpdateTime:
if value, ok := values[i].(*ent2.CustomTime); !ok {
return fmt.Errorf("unexpected type %T for field update_time", values[i])
} else if value != nil {
_m.UpdateTime = value
}
case courseentity.FieldUserID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field user_id", values[i])
} else if value.Valid {
_m.UserID = value.Int64
}
case courseentity.FieldUsername:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field username", values[i])
} else if value.Valid {
_m.Username = value.String
}
case courseentity.FieldPassword:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field password", values[i])
} else if value.Valid {
_m.Password = value.String
}
case courseentity.FieldType:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field type", values[i])
} else if value.Valid {
_m.Type = courseentity.Type(value.String)
}
case courseentity.FieldCourseName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field course_name", values[i])
} else if value.Valid {
_m.CourseName = value.String
}
case courseentity.FieldCourseID:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field course_id", values[i])
} else if value.Valid {
_m.CourseID = value.String
}
case courseentity.FieldNeed:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field need", values[i])
} else if value.Valid {
_m.Need = courseentity.Need(value.String)
}
case courseentity.FieldStatus:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field status", values[i])
} else if value.Valid {
_m.Status = courseentity.Status(value.String)
}
case courseentity.FieldTaskCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field task_count", values[i])
} else if value.Valid {
_m.TaskCount = int(value.Int64)
}
case courseentity.FieldSuccessCount:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field success_count", values[i])
} else if value.Valid {
_m.SuccessCount = int(value.Int64)
}
case courseentity.FieldExamState:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field exam_state", values[i])
} else if value.Valid {
_m.ExamState = courseentity.ExamState(value.String)
}
case courseentity.FieldViolence:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field violence", values[i])
} else if value.Valid {
_m.Violence = courseentity.Violence(value.String)
}
case courseentity.FieldDesc:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field desc", values[i])
} else if value.Valid {
_m.Desc = value.String
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the CourseEntity.
// This includes values selected through modifiers, order, etc.
func (_m *CourseEntity) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QuerySysUser queries the "sys_user" edge of the CourseEntity entity.
func (_m *CourseEntity) QuerySysUser() *SysUserQuery {
return NewCourseEntityClient(_m.config).QuerySysUser(_m)
}
// Update returns a builder for updating this CourseEntity.
// Note that you need to call CourseEntity.Unwrap() before calling this method if this CourseEntity
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *CourseEntity) Update() *CourseEntityUpdateOne {
return NewCourseEntityClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the CourseEntity entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *CourseEntity) Unwrap() *CourseEntity {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: CourseEntity is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *CourseEntity) String() string {
var builder strings.Builder
builder.WriteString("CourseEntity(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("create_time=")
builder.WriteString(fmt.Sprintf("%v", _m.CreateTime))
builder.WriteString(", ")
builder.WriteString("update_time=")
builder.WriteString(fmt.Sprintf("%v", _m.UpdateTime))
builder.WriteString(", ")
builder.WriteString("user_id=")
builder.WriteString(fmt.Sprintf("%v", _m.UserID))
builder.WriteString(", ")
builder.WriteString("username=")
builder.WriteString(_m.Username)
builder.WriteString(", ")
builder.WriteString("password=<sensitive>")
builder.WriteString(", ")
builder.WriteString("type=")
builder.WriteString(fmt.Sprintf("%v", _m.Type))
builder.WriteString(", ")
builder.WriteString("course_name=")
builder.WriteString(_m.CourseName)
builder.WriteString(", ")
builder.WriteString("course_id=")
builder.WriteString(_m.CourseID)
builder.WriteString(", ")
builder.WriteString("need=")
builder.WriteString(fmt.Sprintf("%v", _m.Need))
builder.WriteString(", ")
builder.WriteString("status=")
builder.WriteString(fmt.Sprintf("%v", _m.Status))
builder.WriteString(", ")
builder.WriteString("task_count=")
builder.WriteString(fmt.Sprintf("%v", _m.TaskCount))
builder.WriteString(", ")
builder.WriteString("success_count=")
builder.WriteString(fmt.Sprintf("%v", _m.SuccessCount))
builder.WriteString(", ")
builder.WriteString("exam_state=")
builder.WriteString(fmt.Sprintf("%v", _m.ExamState))
builder.WriteString(", ")
builder.WriteString("violence=")
builder.WriteString(fmt.Sprintf("%v", _m.Violence))
builder.WriteString(", ")
builder.WriteString("desc=")
builder.WriteString(_m.Desc)
builder.WriteByte(')')
return builder.String()
}
// CourseEntities is a parsable slice of CourseEntity.
type CourseEntities []*CourseEntity
+335
View File
@@ -0,0 +1,335 @@
// ENT数据库文件,请勿编辑。
package courseentity
import (
"fmt"
"likedream-goframe/internal/framework/ent2"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the courseentity type in the database.
Label = "course_entity"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldCreateTime holds the string denoting the create_time field in the database.
FieldCreateTime = "create_time"
// FieldUpdateTime holds the string denoting the update_time field in the database.
FieldUpdateTime = "update_time"
// FieldUserID holds the string denoting the user_id field in the database.
FieldUserID = "user_id"
// FieldUsername holds the string denoting the username field in the database.
FieldUsername = "username"
// FieldPassword holds the string denoting the password field in the database.
FieldPassword = "password"
// FieldType holds the string denoting the type field in the database.
FieldType = "type"
// FieldCourseName holds the string denoting the course_name field in the database.
FieldCourseName = "course_name"
// FieldCourseID holds the string denoting the course_id field in the database.
FieldCourseID = "course_id"
// FieldNeed holds the string denoting the need field in the database.
FieldNeed = "need"
// FieldStatus holds the string denoting the status field in the database.
FieldStatus = "status"
// FieldTaskCount holds the string denoting the task_count field in the database.
FieldTaskCount = "task_count"
// FieldSuccessCount holds the string denoting the success_count field in the database.
FieldSuccessCount = "success_count"
// FieldExamState holds the string denoting the exam_state field in the database.
FieldExamState = "exam_state"
// FieldViolence holds the string denoting the violence field in the database.
FieldViolence = "violence"
// FieldDesc holds the string denoting the desc field in the database.
FieldDesc = "desc"
// EdgeSysUser holds the string denoting the sys_user edge name in mutations.
EdgeSysUser = "sys_user"
// Table holds the table name of the courseentity in the database.
Table = "course_entity"
// SysUserTable is the table that holds the sys_user relation/edge.
SysUserTable = "course_entity"
// SysUserInverseTable is the table name for the SysUser entity.
// It exists in this package in order to avoid circular dependency with the "sysuser" package.
SysUserInverseTable = "sys_user"
// SysUserColumn is the table column denoting the sys_user relation/edge.
SysUserColumn = "user_id"
)
// Columns holds all SQL columns for courseentity fields.
var Columns = []string{
FieldID,
FieldCreateTime,
FieldUpdateTime,
FieldUserID,
FieldUsername,
FieldPassword,
FieldType,
FieldCourseName,
FieldCourseID,
FieldNeed,
FieldStatus,
FieldTaskCount,
FieldSuccessCount,
FieldExamState,
FieldViolence,
FieldDesc,
}
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
if column == Columns[i] {
return true
}
}
return false
}
// Note that the variables below are initialized by the runtime
// package on the initialization of the application. Therefore,
// it should be imported in the main as follows:
//
// import _ "likedream-goframe/internal/ent/runtime"
var (
Hooks [1]ent.Hook
// DefaultCreateTime holds the default value on creation for the "create_time" field.
DefaultCreateTime func() *ent2.CustomTime
// DefaultUpdateTime holds the default value on creation for the "update_time" field.
DefaultUpdateTime func() *ent2.CustomTime
// DefaultTaskCount holds the default value on creation for the "task_count" field.
DefaultTaskCount int
// DefaultSuccessCount holds the default value on creation for the "success_count" field.
DefaultSuccessCount int
)
// Type defines the type for the "type" enum field.
type Type string
// Type values.
const (
TypeXxt Type = "xxt"
)
func (_type Type) String() string {
return string(_type)
}
// TypeValidator is a validator for the "type" field enum values. It is called by the builders before save.
func TypeValidator(_type Type) error {
switch _type {
case TypeXxt:
return nil
default:
return fmt.Errorf("courseentity: invalid enum value for type field: %q", _type)
}
}
// Need defines the type for the "need" enum field.
type Need string
// Need values.
const (
NeedTask Need = "task"
NeedExam Need = "exam"
NeedAll Need = "all"
)
func (n Need) String() string {
return string(n)
}
// NeedValidator is a validator for the "need" field enum values. It is called by the builders before save.
func NeedValidator(n Need) error {
switch n {
case NeedTask, NeedExam, NeedAll:
return nil
default:
return fmt.Errorf("courseentity: invalid enum value for need field: %q", n)
}
}
// Status defines the type for the "status" enum field.
type Status string
// StatusNotStart is the default value of the Status enum.
const DefaultStatus = StatusNotStart
// Status values.
const (
StatusNotStart Status = "not_start"
StatusWaiting Status = "waiting"
StatusDoing Status = "doing"
StatusDone Status = "done"
StatusFail Status = "fail"
StatusPause Status = "pause"
)
func (s Status) String() string {
return string(s)
}
// StatusValidator is a validator for the "status" field enum values. It is called by the builders before save.
func StatusValidator(s Status) error {
switch s {
case StatusNotStart, StatusWaiting, StatusDoing, StatusDone, StatusFail, StatusPause:
return nil
default:
return fmt.Errorf("courseentity: invalid enum value for status field: %q", s)
}
}
// ExamState defines the type for the "exam_state" enum field.
type ExamState string
// ExamStateNotStart is the default value of the ExamState enum.
const DefaultExamState = ExamStateNotStart
// ExamState values.
const (
ExamStateNotStart ExamState = "not_start"
ExamStateWaiting ExamState = "waiting"
ExamStateDoing ExamState = "doing"
ExamStateDone ExamState = "done"
ExamStateFail ExamState = "fail"
)
func (es ExamState) String() string {
return string(es)
}
// ExamStateValidator is a validator for the "exam_state" field enum values. It is called by the builders before save.
func ExamStateValidator(es ExamState) error {
switch es {
case ExamStateNotStart, ExamStateWaiting, ExamStateDoing, ExamStateDone, ExamStateFail:
return nil
default:
return fmt.Errorf("courseentity: invalid enum value for exam_state field: %q", es)
}
}
// Violence defines the type for the "violence" enum field.
type Violence string
// ViolenceNo is the default value of the Violence enum.
const DefaultViolence = ViolenceNo
// Violence values.
const (
ViolenceNo Violence = "no"
ViolenceYes Violence = "yes"
)
func (v Violence) String() string {
return string(v)
}
// ViolenceValidator is a validator for the "violence" field enum values. It is called by the builders before save.
func ViolenceValidator(v Violence) error {
switch v {
case ViolenceNo, ViolenceYes:
return nil
default:
return fmt.Errorf("courseentity: invalid enum value for violence field: %q", v)
}
}
// OrderOption defines the ordering options for the CourseEntity queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreateTime orders the results by the create_time field.
func ByCreateTime(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreateTime, opts...).ToFunc()
}
// ByUpdateTime orders the results by the update_time field.
func ByUpdateTime(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdateTime, opts...).ToFunc()
}
// ByUserID orders the results by the user_id field.
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUserID, opts...).ToFunc()
}
// ByUsername orders the results by the username field.
func ByUsername(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUsername, opts...).ToFunc()
}
// ByPassword orders the results by the password field.
func ByPassword(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPassword, opts...).ToFunc()
}
// ByType orders the results by the type field.
func ByType(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldType, opts...).ToFunc()
}
// ByCourseName orders the results by the course_name field.
func ByCourseName(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCourseName, opts...).ToFunc()
}
// ByCourseID orders the results by the course_id field.
func ByCourseID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCourseID, opts...).ToFunc()
}
// ByNeed orders the results by the need field.
func ByNeed(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldNeed, opts...).ToFunc()
}
// ByStatus orders the results by the status field.
func ByStatus(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldStatus, opts...).ToFunc()
}
// ByTaskCount orders the results by the task_count field.
func ByTaskCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldTaskCount, opts...).ToFunc()
}
// BySuccessCount orders the results by the success_count field.
func BySuccessCount(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSuccessCount, opts...).ToFunc()
}
// ByExamState orders the results by the exam_state field.
func ByExamState(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldExamState, opts...).ToFunc()
}
// ByViolence orders the results by the violence field.
func ByViolence(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldViolence, opts...).ToFunc()
}
// ByDesc orders the results by the desc field.
func ByDesc(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDesc, opts...).ToFunc()
}
// BySysUserField orders the results by sys_user field.
func BySysUserField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newSysUserStep(), sql.OrderByField(field, opts...))
}
}
func newSysUserStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(SysUserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, SysUserTable, SysUserColumn),
)
}
+826
View File
@@ -0,0 +1,826 @@
// ENT数据库文件,请勿编辑。
package courseentity
import (
"likedream-goframe/internal/ent/internal"
"likedream-goframe/internal/ent/predicate"
"likedream-goframe/internal/framework/ent2"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldID, id))
}
// CreateTime applies equality check predicate on the "create_time" field. It's identical to CreateTimeEQ.
func CreateTime(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCreateTime, v))
}
// UpdateTime applies equality check predicate on the "update_time" field. It's identical to UpdateTimeEQ.
func UpdateTime(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUpdateTime, v))
}
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
func UserID(v int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUserID, v))
}
// Username applies equality check predicate on the "username" field. It's identical to UsernameEQ.
func Username(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUsername, v))
}
// Password applies equality check predicate on the "password" field. It's identical to PasswordEQ.
func Password(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldPassword, v))
}
// CourseName applies equality check predicate on the "course_name" field. It's identical to CourseNameEQ.
func CourseName(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCourseName, v))
}
// CourseID applies equality check predicate on the "course_id" field. It's identical to CourseIDEQ.
func CourseID(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCourseID, v))
}
// TaskCount applies equality check predicate on the "task_count" field. It's identical to TaskCountEQ.
func TaskCount(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldTaskCount, v))
}
// SuccessCount applies equality check predicate on the "success_count" field. It's identical to SuccessCountEQ.
func SuccessCount(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldSuccessCount, v))
}
// Desc applies equality check predicate on the "desc" field. It's identical to DescEQ.
func Desc(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldDesc, v))
}
// CreateTimeEQ applies the EQ predicate on the "create_time" field.
func CreateTimeEQ(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCreateTime, v))
}
// CreateTimeNEQ applies the NEQ predicate on the "create_time" field.
func CreateTimeNEQ(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldCreateTime, v))
}
// CreateTimeIn applies the In predicate on the "create_time" field.
func CreateTimeIn(vs ...*ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldCreateTime, vs...))
}
// CreateTimeNotIn applies the NotIn predicate on the "create_time" field.
func CreateTimeNotIn(vs ...*ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldCreateTime, vs...))
}
// CreateTimeGT applies the GT predicate on the "create_time" field.
func CreateTimeGT(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldCreateTime, v))
}
// CreateTimeGTE applies the GTE predicate on the "create_time" field.
func CreateTimeGTE(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldCreateTime, v))
}
// CreateTimeLT applies the LT predicate on the "create_time" field.
func CreateTimeLT(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldCreateTime, v))
}
// CreateTimeLTE applies the LTE predicate on the "create_time" field.
func CreateTimeLTE(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldCreateTime, v))
}
// CreateTimeContains applies the Contains predicate on the "create_time" field.
func CreateTimeContains(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldContains(FieldCreateTime, vc))
}
// CreateTimeHasPrefix applies the HasPrefix predicate on the "create_time" field.
func CreateTimeHasPrefix(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldHasPrefix(FieldCreateTime, vc))
}
// CreateTimeHasSuffix applies the HasSuffix predicate on the "create_time" field.
func CreateTimeHasSuffix(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldHasSuffix(FieldCreateTime, vc))
}
// CreateTimeEqualFold applies the EqualFold predicate on the "create_time" field.
func CreateTimeEqualFold(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldEqualFold(FieldCreateTime, vc))
}
// CreateTimeContainsFold applies the ContainsFold predicate on the "create_time" field.
func CreateTimeContainsFold(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldContainsFold(FieldCreateTime, vc))
}
// UpdateTimeEQ applies the EQ predicate on the "update_time" field.
func UpdateTimeEQ(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUpdateTime, v))
}
// UpdateTimeNEQ applies the NEQ predicate on the "update_time" field.
func UpdateTimeNEQ(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldUpdateTime, v))
}
// UpdateTimeIn applies the In predicate on the "update_time" field.
func UpdateTimeIn(vs ...*ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldUpdateTime, vs...))
}
// UpdateTimeNotIn applies the NotIn predicate on the "update_time" field.
func UpdateTimeNotIn(vs ...*ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldUpdateTime, vs...))
}
// UpdateTimeGT applies the GT predicate on the "update_time" field.
func UpdateTimeGT(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldUpdateTime, v))
}
// UpdateTimeGTE applies the GTE predicate on the "update_time" field.
func UpdateTimeGTE(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldUpdateTime, v))
}
// UpdateTimeLT applies the LT predicate on the "update_time" field.
func UpdateTimeLT(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldUpdateTime, v))
}
// UpdateTimeLTE applies the LTE predicate on the "update_time" field.
func UpdateTimeLTE(v *ent2.CustomTime) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldUpdateTime, v))
}
// UpdateTimeContains applies the Contains predicate on the "update_time" field.
func UpdateTimeContains(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldContains(FieldUpdateTime, vc))
}
// UpdateTimeHasPrefix applies the HasPrefix predicate on the "update_time" field.
func UpdateTimeHasPrefix(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldHasPrefix(FieldUpdateTime, vc))
}
// UpdateTimeHasSuffix applies the HasSuffix predicate on the "update_time" field.
func UpdateTimeHasSuffix(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldHasSuffix(FieldUpdateTime, vc))
}
// UpdateTimeEqualFold applies the EqualFold predicate on the "update_time" field.
func UpdateTimeEqualFold(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldEqualFold(FieldUpdateTime, vc))
}
// UpdateTimeContainsFold applies the ContainsFold predicate on the "update_time" field.
func UpdateTimeContainsFold(v *ent2.CustomTime) predicate.CourseEntity {
vc := v.String()
return predicate.CourseEntity(sql.FieldContainsFold(FieldUpdateTime, vc))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUserID, v))
}
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
func UserIDNEQ(v int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldUserID, v))
}
// UserIDIn applies the In predicate on the "user_id" field.
func UserIDIn(vs ...int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldUserID, vs...))
}
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
func UserIDNotIn(vs ...int64) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldUserID, vs...))
}
// UsernameEQ applies the EQ predicate on the "username" field.
func UsernameEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldUsername, v))
}
// UsernameNEQ applies the NEQ predicate on the "username" field.
func UsernameNEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldUsername, v))
}
// UsernameIn applies the In predicate on the "username" field.
func UsernameIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldUsername, vs...))
}
// UsernameNotIn applies the NotIn predicate on the "username" field.
func UsernameNotIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldUsername, vs...))
}
// UsernameGT applies the GT predicate on the "username" field.
func UsernameGT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldUsername, v))
}
// UsernameGTE applies the GTE predicate on the "username" field.
func UsernameGTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldUsername, v))
}
// UsernameLT applies the LT predicate on the "username" field.
func UsernameLT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldUsername, v))
}
// UsernameLTE applies the LTE predicate on the "username" field.
func UsernameLTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldUsername, v))
}
// UsernameContains applies the Contains predicate on the "username" field.
func UsernameContains(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContains(FieldUsername, v))
}
// UsernameHasPrefix applies the HasPrefix predicate on the "username" field.
func UsernameHasPrefix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasPrefix(FieldUsername, v))
}
// UsernameHasSuffix applies the HasSuffix predicate on the "username" field.
func UsernameHasSuffix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasSuffix(FieldUsername, v))
}
// UsernameEqualFold applies the EqualFold predicate on the "username" field.
func UsernameEqualFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEqualFold(FieldUsername, v))
}
// UsernameContainsFold applies the ContainsFold predicate on the "username" field.
func UsernameContainsFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContainsFold(FieldUsername, v))
}
// PasswordEQ applies the EQ predicate on the "password" field.
func PasswordEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldPassword, v))
}
// PasswordNEQ applies the NEQ predicate on the "password" field.
func PasswordNEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldPassword, v))
}
// PasswordIn applies the In predicate on the "password" field.
func PasswordIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldPassword, vs...))
}
// PasswordNotIn applies the NotIn predicate on the "password" field.
func PasswordNotIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldPassword, vs...))
}
// PasswordGT applies the GT predicate on the "password" field.
func PasswordGT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldPassword, v))
}
// PasswordGTE applies the GTE predicate on the "password" field.
func PasswordGTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldPassword, v))
}
// PasswordLT applies the LT predicate on the "password" field.
func PasswordLT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldPassword, v))
}
// PasswordLTE applies the LTE predicate on the "password" field.
func PasswordLTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldPassword, v))
}
// PasswordContains applies the Contains predicate on the "password" field.
func PasswordContains(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContains(FieldPassword, v))
}
// PasswordHasPrefix applies the HasPrefix predicate on the "password" field.
func PasswordHasPrefix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasPrefix(FieldPassword, v))
}
// PasswordHasSuffix applies the HasSuffix predicate on the "password" field.
func PasswordHasSuffix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasSuffix(FieldPassword, v))
}
// PasswordEqualFold applies the EqualFold predicate on the "password" field.
func PasswordEqualFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEqualFold(FieldPassword, v))
}
// PasswordContainsFold applies the ContainsFold predicate on the "password" field.
func PasswordContainsFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContainsFold(FieldPassword, v))
}
// TypeEQ applies the EQ predicate on the "type" field.
func TypeEQ(v Type) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldType, v))
}
// TypeNEQ applies the NEQ predicate on the "type" field.
func TypeNEQ(v Type) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldType, v))
}
// TypeIn applies the In predicate on the "type" field.
func TypeIn(vs ...Type) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldType, vs...))
}
// TypeNotIn applies the NotIn predicate on the "type" field.
func TypeNotIn(vs ...Type) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldType, vs...))
}
// CourseNameEQ applies the EQ predicate on the "course_name" field.
func CourseNameEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCourseName, v))
}
// CourseNameNEQ applies the NEQ predicate on the "course_name" field.
func CourseNameNEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldCourseName, v))
}
// CourseNameIn applies the In predicate on the "course_name" field.
func CourseNameIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldCourseName, vs...))
}
// CourseNameNotIn applies the NotIn predicate on the "course_name" field.
func CourseNameNotIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldCourseName, vs...))
}
// CourseNameGT applies the GT predicate on the "course_name" field.
func CourseNameGT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldCourseName, v))
}
// CourseNameGTE applies the GTE predicate on the "course_name" field.
func CourseNameGTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldCourseName, v))
}
// CourseNameLT applies the LT predicate on the "course_name" field.
func CourseNameLT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldCourseName, v))
}
// CourseNameLTE applies the LTE predicate on the "course_name" field.
func CourseNameLTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldCourseName, v))
}
// CourseNameContains applies the Contains predicate on the "course_name" field.
func CourseNameContains(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContains(FieldCourseName, v))
}
// CourseNameHasPrefix applies the HasPrefix predicate on the "course_name" field.
func CourseNameHasPrefix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasPrefix(FieldCourseName, v))
}
// CourseNameHasSuffix applies the HasSuffix predicate on the "course_name" field.
func CourseNameHasSuffix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasSuffix(FieldCourseName, v))
}
// CourseNameEqualFold applies the EqualFold predicate on the "course_name" field.
func CourseNameEqualFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEqualFold(FieldCourseName, v))
}
// CourseNameContainsFold applies the ContainsFold predicate on the "course_name" field.
func CourseNameContainsFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContainsFold(FieldCourseName, v))
}
// CourseIDEQ applies the EQ predicate on the "course_id" field.
func CourseIDEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldCourseID, v))
}
// CourseIDNEQ applies the NEQ predicate on the "course_id" field.
func CourseIDNEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldCourseID, v))
}
// CourseIDIn applies the In predicate on the "course_id" field.
func CourseIDIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldCourseID, vs...))
}
// CourseIDNotIn applies the NotIn predicate on the "course_id" field.
func CourseIDNotIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldCourseID, vs...))
}
// CourseIDGT applies the GT predicate on the "course_id" field.
func CourseIDGT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldCourseID, v))
}
// CourseIDGTE applies the GTE predicate on the "course_id" field.
func CourseIDGTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldCourseID, v))
}
// CourseIDLT applies the LT predicate on the "course_id" field.
func CourseIDLT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldCourseID, v))
}
// CourseIDLTE applies the LTE predicate on the "course_id" field.
func CourseIDLTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldCourseID, v))
}
// CourseIDContains applies the Contains predicate on the "course_id" field.
func CourseIDContains(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContains(FieldCourseID, v))
}
// CourseIDHasPrefix applies the HasPrefix predicate on the "course_id" field.
func CourseIDHasPrefix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasPrefix(FieldCourseID, v))
}
// CourseIDHasSuffix applies the HasSuffix predicate on the "course_id" field.
func CourseIDHasSuffix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasSuffix(FieldCourseID, v))
}
// CourseIDEqualFold applies the EqualFold predicate on the "course_id" field.
func CourseIDEqualFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEqualFold(FieldCourseID, v))
}
// CourseIDContainsFold applies the ContainsFold predicate on the "course_id" field.
func CourseIDContainsFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContainsFold(FieldCourseID, v))
}
// NeedEQ applies the EQ predicate on the "need" field.
func NeedEQ(v Need) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldNeed, v))
}
// NeedNEQ applies the NEQ predicate on the "need" field.
func NeedNEQ(v Need) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldNeed, v))
}
// NeedIn applies the In predicate on the "need" field.
func NeedIn(vs ...Need) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldNeed, vs...))
}
// NeedNotIn applies the NotIn predicate on the "need" field.
func NeedNotIn(vs ...Need) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldNeed, vs...))
}
// StatusEQ applies the EQ predicate on the "status" field.
func StatusEQ(v Status) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldStatus, v))
}
// StatusNEQ applies the NEQ predicate on the "status" field.
func StatusNEQ(v Status) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldStatus, v))
}
// StatusIn applies the In predicate on the "status" field.
func StatusIn(vs ...Status) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldStatus, vs...))
}
// StatusNotIn applies the NotIn predicate on the "status" field.
func StatusNotIn(vs ...Status) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldStatus, vs...))
}
// TaskCountEQ applies the EQ predicate on the "task_count" field.
func TaskCountEQ(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldTaskCount, v))
}
// TaskCountNEQ applies the NEQ predicate on the "task_count" field.
func TaskCountNEQ(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldTaskCount, v))
}
// TaskCountIn applies the In predicate on the "task_count" field.
func TaskCountIn(vs ...int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldTaskCount, vs...))
}
// TaskCountNotIn applies the NotIn predicate on the "task_count" field.
func TaskCountNotIn(vs ...int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldTaskCount, vs...))
}
// TaskCountGT applies the GT predicate on the "task_count" field.
func TaskCountGT(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldTaskCount, v))
}
// TaskCountGTE applies the GTE predicate on the "task_count" field.
func TaskCountGTE(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldTaskCount, v))
}
// TaskCountLT applies the LT predicate on the "task_count" field.
func TaskCountLT(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldTaskCount, v))
}
// TaskCountLTE applies the LTE predicate on the "task_count" field.
func TaskCountLTE(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldTaskCount, v))
}
// SuccessCountEQ applies the EQ predicate on the "success_count" field.
func SuccessCountEQ(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldSuccessCount, v))
}
// SuccessCountNEQ applies the NEQ predicate on the "success_count" field.
func SuccessCountNEQ(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldSuccessCount, v))
}
// SuccessCountIn applies the In predicate on the "success_count" field.
func SuccessCountIn(vs ...int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldSuccessCount, vs...))
}
// SuccessCountNotIn applies the NotIn predicate on the "success_count" field.
func SuccessCountNotIn(vs ...int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldSuccessCount, vs...))
}
// SuccessCountGT applies the GT predicate on the "success_count" field.
func SuccessCountGT(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldSuccessCount, v))
}
// SuccessCountGTE applies the GTE predicate on the "success_count" field.
func SuccessCountGTE(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldSuccessCount, v))
}
// SuccessCountLT applies the LT predicate on the "success_count" field.
func SuccessCountLT(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldSuccessCount, v))
}
// SuccessCountLTE applies the LTE predicate on the "success_count" field.
func SuccessCountLTE(v int) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldSuccessCount, v))
}
// ExamStateEQ applies the EQ predicate on the "exam_state" field.
func ExamStateEQ(v ExamState) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldExamState, v))
}
// ExamStateNEQ applies the NEQ predicate on the "exam_state" field.
func ExamStateNEQ(v ExamState) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldExamState, v))
}
// ExamStateIn applies the In predicate on the "exam_state" field.
func ExamStateIn(vs ...ExamState) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldExamState, vs...))
}
// ExamStateNotIn applies the NotIn predicate on the "exam_state" field.
func ExamStateNotIn(vs ...ExamState) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldExamState, vs...))
}
// ViolenceEQ applies the EQ predicate on the "violence" field.
func ViolenceEQ(v Violence) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldViolence, v))
}
// ViolenceNEQ applies the NEQ predicate on the "violence" field.
func ViolenceNEQ(v Violence) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldViolence, v))
}
// ViolenceIn applies the In predicate on the "violence" field.
func ViolenceIn(vs ...Violence) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldViolence, vs...))
}
// ViolenceNotIn applies the NotIn predicate on the "violence" field.
func ViolenceNotIn(vs ...Violence) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldViolence, vs...))
}
// DescEQ applies the EQ predicate on the "desc" field.
func DescEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEQ(FieldDesc, v))
}
// DescNEQ applies the NEQ predicate on the "desc" field.
func DescNEQ(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNEQ(FieldDesc, v))
}
// DescIn applies the In predicate on the "desc" field.
func DescIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIn(FieldDesc, vs...))
}
// DescNotIn applies the NotIn predicate on the "desc" field.
func DescNotIn(vs ...string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotIn(FieldDesc, vs...))
}
// DescGT applies the GT predicate on the "desc" field.
func DescGT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGT(FieldDesc, v))
}
// DescGTE applies the GTE predicate on the "desc" field.
func DescGTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldGTE(FieldDesc, v))
}
// DescLT applies the LT predicate on the "desc" field.
func DescLT(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLT(FieldDesc, v))
}
// DescLTE applies the LTE predicate on the "desc" field.
func DescLTE(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldLTE(FieldDesc, v))
}
// DescContains applies the Contains predicate on the "desc" field.
func DescContains(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContains(FieldDesc, v))
}
// DescHasPrefix applies the HasPrefix predicate on the "desc" field.
func DescHasPrefix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasPrefix(FieldDesc, v))
}
// DescHasSuffix applies the HasSuffix predicate on the "desc" field.
func DescHasSuffix(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldHasSuffix(FieldDesc, v))
}
// DescIsNil applies the IsNil predicate on the "desc" field.
func DescIsNil() predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldIsNull(FieldDesc))
}
// DescNotNil applies the NotNil predicate on the "desc" field.
func DescNotNil() predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldNotNull(FieldDesc))
}
// DescEqualFold applies the EqualFold predicate on the "desc" field.
func DescEqualFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldEqualFold(FieldDesc, v))
}
// DescContainsFold applies the ContainsFold predicate on the "desc" field.
func DescContainsFold(v string) predicate.CourseEntity {
return predicate.CourseEntity(sql.FieldContainsFold(FieldDesc, v))
}
// HasSysUser applies the HasEdge predicate on the "sys_user" edge.
func HasSysUser() predicate.CourseEntity {
return predicate.CourseEntity(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, SysUserTable, SysUserColumn),
)
schemaConfig := internal.SchemaConfigFromContext(s.Context())
step.To.Schema = schemaConfig.SysUser
step.Edge.Schema = schemaConfig.CourseEntity
sqlgraph.HasNeighbors(s, step)
})
}
// HasSysUserWith applies the HasEdge predicate on the "sys_user" edge with a given conditions (other predicates).
func HasSysUserWith(preds ...predicate.SysUser) predicate.CourseEntity {
return predicate.CourseEntity(func(s *sql.Selector) {
step := newSysUserStep()
schemaConfig := internal.SchemaConfigFromContext(s.Context())
step.To.Schema = schemaConfig.SysUser
step.Edge.Schema = schemaConfig.CourseEntity
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.CourseEntity) predicate.CourseEntity {
return predicate.CourseEntity(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.CourseEntity) predicate.CourseEntity {
return predicate.CourseEntity(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.CourseEntity) predicate.CourseEntity {
return predicate.CourseEntity(sql.NotPredicates(p))
}
File diff suppressed because it is too large Load Diff
+91
View File
@@ -0,0 +1,91 @@
// ENT数据库文件,请勿编辑。
package ent
import (
"context"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/internal"
"likedream-goframe/internal/ent/predicate"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
)
// CourseEntityDelete is the builder for deleting a CourseEntity entity.
type CourseEntityDelete struct {
config
hooks []Hook
mutation *CourseEntityMutation
}
// Where appends a list predicates to the CourseEntityDelete builder.
func (_d *CourseEntityDelete) Where(ps ...predicate.CourseEntity) *CourseEntityDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *CourseEntityDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *CourseEntityDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *CourseEntityDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(courseentity.Table, sqlgraph.NewFieldSpec(courseentity.FieldID, field.TypeInt64))
_spec.Node.Schema = _d.schemaConfig.CourseEntity
ctx = internal.NewSchemaConfigContext(ctx, _d.schemaConfig)
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// CourseEntityDeleteOne is the builder for deleting a single CourseEntity entity.
type CourseEntityDeleteOne struct {
_d *CourseEntityDelete
}
// Where appends a list predicates to the CourseEntityDelete builder.
func (_d *CourseEntityDeleteOne) Where(ps ...predicate.CourseEntity) *CourseEntityDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *CourseEntityDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{courseentity.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *CourseEntityDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}
+667
View File
@@ -0,0 +1,667 @@
// ENT数据库文件,请勿编辑。
package ent
import (
"context"
"fmt"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/internal"
"likedream-goframe/internal/ent/predicate"
"likedream-goframe/internal/ent/sysuser"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
)
// CourseEntityQuery is the builder for querying CourseEntity entities.
type CourseEntityQuery struct {
config
ctx *QueryContext
order []courseentity.OrderOption
inters []Interceptor
predicates []predicate.CourseEntity
withSysUser *SysUserQuery
modifiers []func(*sql.Selector)
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the CourseEntityQuery builder.
func (_q *CourseEntityQuery) Where(ps ...predicate.CourseEntity) *CourseEntityQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *CourseEntityQuery) Limit(limit int) *CourseEntityQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *CourseEntityQuery) Offset(offset int) *CourseEntityQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *CourseEntityQuery) Unique(unique bool) *CourseEntityQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *CourseEntityQuery) Order(o ...courseentity.OrderOption) *CourseEntityQuery {
_q.order = append(_q.order, o...)
return _q
}
// QuerySysUser chains the current query on the "sys_user" edge.
func (_q *CourseEntityQuery) QuerySysUser() *SysUserQuery {
query := (&SysUserClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(courseentity.Table, courseentity.FieldID, selector),
sqlgraph.To(sysuser.Table, sysuser.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, courseentity.SysUserTable, courseentity.SysUserColumn),
)
schemaConfig := _q.schemaConfig
step.To.Schema = schemaConfig.SysUser
step.Edge.Schema = schemaConfig.CourseEntity
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first CourseEntity entity from the query.
// Returns a *NotFoundError when no CourseEntity was found.
func (_q *CourseEntityQuery) First(ctx context.Context) (*CourseEntity, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{courseentity.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *CourseEntityQuery) FirstX(ctx context.Context) *CourseEntity {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first CourseEntity ID from the query.
// Returns a *NotFoundError when no CourseEntity ID was found.
func (_q *CourseEntityQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{courseentity.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *CourseEntityQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single CourseEntity entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one CourseEntity entity is found.
// Returns a *NotFoundError when no CourseEntity entities are found.
func (_q *CourseEntityQuery) Only(ctx context.Context) (*CourseEntity, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{courseentity.Label}
default:
return nil, &NotSingularError{courseentity.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *CourseEntityQuery) OnlyX(ctx context.Context) *CourseEntity {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only CourseEntity ID in the query.
// Returns a *NotSingularError when more than one CourseEntity ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *CourseEntityQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{courseentity.Label}
default:
err = &NotSingularError{courseentity.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *CourseEntityQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of CourseEntities.
func (_q *CourseEntityQuery) All(ctx context.Context) ([]*CourseEntity, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*CourseEntity, *CourseEntityQuery]()
return withInterceptors[[]*CourseEntity](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *CourseEntityQuery) AllX(ctx context.Context) []*CourseEntity {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of CourseEntity IDs.
func (_q *CourseEntityQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(courseentity.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *CourseEntityQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *CourseEntityQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*CourseEntityQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *CourseEntityQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *CourseEntityQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *CourseEntityQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the CourseEntityQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *CourseEntityQuery) Clone() *CourseEntityQuery {
if _q == nil {
return nil
}
return &CourseEntityQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]courseentity.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.CourseEntity{}, _q.predicates...),
withSysUser: _q.withSysUser.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
modifiers: append([]func(*sql.Selector){}, _q.modifiers...),
}
}
// WithSysUser tells the query-builder to eager-load the nodes that are connected to
// the "sys_user" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *CourseEntityQuery) WithSysUser(opts ...func(*SysUserQuery)) *CourseEntityQuery {
query := (&SysUserClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withSysUser = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreateTime *ent2.CustomTime `json:"createTime"`
// Count int `json:"count,omitempty"`
// }
//
// client.CourseEntity.Query().
// GroupBy(courseentity.FieldCreateTime).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *CourseEntityQuery) GroupBy(field string, fields ...string) *CourseEntityGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &CourseEntityGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = courseentity.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreateTime *ent2.CustomTime `json:"createTime"`
// }
//
// client.CourseEntity.Query().
// Select(courseentity.FieldCreateTime).
// Scan(ctx, &v)
func (_q *CourseEntityQuery) Select(fields ...string) *CourseEntitySelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &CourseEntitySelect{CourseEntityQuery: _q}
sbuild.label = courseentity.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a CourseEntitySelect configured with the given aggregations.
func (_q *CourseEntityQuery) Aggregate(fns ...AggregateFunc) *CourseEntitySelect {
return _q.Select().Aggregate(fns...)
}
func (_q *CourseEntityQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !courseentity.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *CourseEntityQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*CourseEntity, error) {
var (
nodes = []*CourseEntity{}
_spec = _q.querySpec()
loadedTypes = [1]bool{
_q.withSysUser != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*CourseEntity).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &CourseEntity{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
_spec.Node.Schema = _q.schemaConfig.CourseEntity
ctx = internal.NewSchemaConfigContext(ctx, _q.schemaConfig)
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withSysUser; query != nil {
if err := _q.loadSysUser(ctx, query, nodes, nil,
func(n *CourseEntity, e *SysUser) { n.Edges.SysUser = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *CourseEntityQuery) loadSysUser(ctx context.Context, query *SysUserQuery, nodes []*CourseEntity, init func(*CourseEntity), assign func(*CourseEntity, *SysUser)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*CourseEntity)
for i := range nodes {
fk := nodes[i].UserID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(sysuser.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *CourseEntityQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
_spec.Node.Schema = _q.schemaConfig.CourseEntity
ctx = internal.NewSchemaConfigContext(ctx, _q.schemaConfig)
if len(_q.modifiers) > 0 {
_spec.Modifiers = _q.modifiers
}
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *CourseEntityQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(courseentity.Table, courseentity.Columns, sqlgraph.NewFieldSpec(courseentity.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, courseentity.FieldID)
for i := range fields {
if fields[i] != courseentity.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if _q.withSysUser != nil {
_spec.Node.AddColumnOnce(courseentity.FieldUserID)
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *CourseEntityQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(courseentity.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = courseentity.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
t1.Schema(_q.schemaConfig.CourseEntity)
ctx = internal.NewSchemaConfigContext(ctx, _q.schemaConfig)
selector.WithContext(ctx)
for _, m := range _q.modifiers {
m(selector)
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
// either committed or rolled-back.
func (_q *CourseEntityQuery) ForUpdate(opts ...sql.LockOption) *CourseEntityQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForUpdate(opts...)
})
return _q
}
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
// on any rows that are read. Other sessions can read the rows, but cannot modify them
// until your transaction commits.
func (_q *CourseEntityQuery) ForShare(opts ...sql.LockOption) *CourseEntityQuery {
if _q.driver.Dialect() == dialect.Postgres {
_q.Unique(false)
}
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
s.ForShare(opts...)
})
return _q
}
// Modify adds a query modifier for attaching custom logic to queries.
func (_q *CourseEntityQuery) Modify(modifiers ...func(s *sql.Selector)) *CourseEntitySelect {
_q.modifiers = append(_q.modifiers, modifiers...)
return _q.Select()
}
// CourseEntityGroupBy is the group-by builder for CourseEntity entities.
type CourseEntityGroupBy struct {
selector
build *CourseEntityQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *CourseEntityGroupBy) Aggregate(fns ...AggregateFunc) *CourseEntityGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *CourseEntityGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*CourseEntityQuery, *CourseEntityGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *CourseEntityGroupBy) sqlScan(ctx context.Context, root *CourseEntityQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// CourseEntitySelect is the builder for selecting fields of CourseEntity entities.
type CourseEntitySelect struct {
*CourseEntityQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *CourseEntitySelect) Aggregate(fns ...AggregateFunc) *CourseEntitySelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *CourseEntitySelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*CourseEntityQuery, *CourseEntitySelect](ctx, _s.CourseEntityQuery, _s, _s.inters, v)
}
func (_s *CourseEntitySelect) sqlScan(ctx context.Context, root *CourseEntityQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// Modify adds a query modifier for attaching custom logic to queries.
func (_s *CourseEntitySelect) Modify(modifiers ...func(s *sql.Selector)) *CourseEntitySelect {
_s.modifiers = append(_s.modifiers, modifiers...)
return _s
}
+878
View File
@@ -0,0 +1,878 @@
// ENT数据库文件,请勿编辑。
package ent
import (
"context"
"errors"
"fmt"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/internal"
"likedream-goframe/internal/ent/predicate"
"likedream-goframe/internal/ent/sysuser"
"likedream-goframe/internal/framework/ent2"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
)
// CourseEntityUpdate is the builder for updating CourseEntity entities.
type CourseEntityUpdate struct {
config
hooks []Hook
mutation *CourseEntityMutation
modifiers []func(*sql.UpdateBuilder)
}
// Where appends a list predicates to the CourseEntityUpdate builder.
func (_u *CourseEntityUpdate) Where(ps ...predicate.CourseEntity) *CourseEntityUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetUpdateTime sets the "update_time" field.
func (_u *CourseEntityUpdate) SetUpdateTime(v *ent2.CustomTime) *CourseEntityUpdate {
_u.mutation.SetUpdateTime(v)
return _u
}
// SetUserID sets the "user_id" field.
func (_u *CourseEntityUpdate) SetUserID(v int64) *CourseEntityUpdate {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableUserID(v *int64) *CourseEntityUpdate {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetUsername sets the "username" field.
func (_u *CourseEntityUpdate) SetUsername(v string) *CourseEntityUpdate {
_u.mutation.SetUsername(v)
return _u
}
// SetNillableUsername sets the "username" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableUsername(v *string) *CourseEntityUpdate {
if v != nil {
_u.SetUsername(*v)
}
return _u
}
// SetPassword sets the "password" field.
func (_u *CourseEntityUpdate) SetPassword(v string) *CourseEntityUpdate {
_u.mutation.SetPassword(v)
return _u
}
// SetNillablePassword sets the "password" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillablePassword(v *string) *CourseEntityUpdate {
if v != nil {
_u.SetPassword(*v)
}
return _u
}
// SetType sets the "type" field.
func (_u *CourseEntityUpdate) SetType(v courseentity.Type) *CourseEntityUpdate {
_u.mutation.SetType(v)
return _u
}
// SetNillableType sets the "type" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableType(v *courseentity.Type) *CourseEntityUpdate {
if v != nil {
_u.SetType(*v)
}
return _u
}
// SetCourseName sets the "course_name" field.
func (_u *CourseEntityUpdate) SetCourseName(v string) *CourseEntityUpdate {
_u.mutation.SetCourseName(v)
return _u
}
// SetNillableCourseName sets the "course_name" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableCourseName(v *string) *CourseEntityUpdate {
if v != nil {
_u.SetCourseName(*v)
}
return _u
}
// SetCourseID sets the "course_id" field.
func (_u *CourseEntityUpdate) SetCourseID(v string) *CourseEntityUpdate {
_u.mutation.SetCourseID(v)
return _u
}
// SetNillableCourseID sets the "course_id" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableCourseID(v *string) *CourseEntityUpdate {
if v != nil {
_u.SetCourseID(*v)
}
return _u
}
// SetNeed sets the "need" field.
func (_u *CourseEntityUpdate) SetNeed(v courseentity.Need) *CourseEntityUpdate {
_u.mutation.SetNeed(v)
return _u
}
// SetNillableNeed sets the "need" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableNeed(v *courseentity.Need) *CourseEntityUpdate {
if v != nil {
_u.SetNeed(*v)
}
return _u
}
// SetStatus sets the "status" field.
func (_u *CourseEntityUpdate) SetStatus(v courseentity.Status) *CourseEntityUpdate {
_u.mutation.SetStatus(v)
return _u
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableStatus(v *courseentity.Status) *CourseEntityUpdate {
if v != nil {
_u.SetStatus(*v)
}
return _u
}
// SetTaskCount sets the "task_count" field.
func (_u *CourseEntityUpdate) SetTaskCount(v int) *CourseEntityUpdate {
_u.mutation.ResetTaskCount()
_u.mutation.SetTaskCount(v)
return _u
}
// SetNillableTaskCount sets the "task_count" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableTaskCount(v *int) *CourseEntityUpdate {
if v != nil {
_u.SetTaskCount(*v)
}
return _u
}
// AddTaskCount adds value to the "task_count" field.
func (_u *CourseEntityUpdate) AddTaskCount(v int) *CourseEntityUpdate {
_u.mutation.AddTaskCount(v)
return _u
}
// SetSuccessCount sets the "success_count" field.
func (_u *CourseEntityUpdate) SetSuccessCount(v int) *CourseEntityUpdate {
_u.mutation.ResetSuccessCount()
_u.mutation.SetSuccessCount(v)
return _u
}
// SetNillableSuccessCount sets the "success_count" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableSuccessCount(v *int) *CourseEntityUpdate {
if v != nil {
_u.SetSuccessCount(*v)
}
return _u
}
// AddSuccessCount adds value to the "success_count" field.
func (_u *CourseEntityUpdate) AddSuccessCount(v int) *CourseEntityUpdate {
_u.mutation.AddSuccessCount(v)
return _u
}
// SetExamState sets the "exam_state" field.
func (_u *CourseEntityUpdate) SetExamState(v courseentity.ExamState) *CourseEntityUpdate {
_u.mutation.SetExamState(v)
return _u
}
// SetNillableExamState sets the "exam_state" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableExamState(v *courseentity.ExamState) *CourseEntityUpdate {
if v != nil {
_u.SetExamState(*v)
}
return _u
}
// SetViolence sets the "violence" field.
func (_u *CourseEntityUpdate) SetViolence(v courseentity.Violence) *CourseEntityUpdate {
_u.mutation.SetViolence(v)
return _u
}
// SetNillableViolence sets the "violence" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableViolence(v *courseentity.Violence) *CourseEntityUpdate {
if v != nil {
_u.SetViolence(*v)
}
return _u
}
// SetDesc sets the "desc" field.
func (_u *CourseEntityUpdate) SetDesc(v string) *CourseEntityUpdate {
_u.mutation.SetDesc(v)
return _u
}
// SetNillableDesc sets the "desc" field if the given value is not nil.
func (_u *CourseEntityUpdate) SetNillableDesc(v *string) *CourseEntityUpdate {
if v != nil {
_u.SetDesc(*v)
}
return _u
}
// ClearDesc clears the value of the "desc" field.
func (_u *CourseEntityUpdate) ClearDesc() *CourseEntityUpdate {
_u.mutation.ClearDesc()
return _u
}
// SetSysUserID sets the "sys_user" edge to the SysUser entity by ID.
func (_u *CourseEntityUpdate) SetSysUserID(id int64) *CourseEntityUpdate {
_u.mutation.SetSysUserID(id)
return _u
}
// SetSysUser sets the "sys_user" edge to the SysUser entity.
func (_u *CourseEntityUpdate) SetSysUser(v *SysUser) *CourseEntityUpdate {
return _u.SetSysUserID(v.ID)
}
// Mutation returns the CourseEntityMutation object of the builder.
func (_u *CourseEntityUpdate) Mutation() *CourseEntityMutation {
return _u.mutation
}
// ClearSysUser clears the "sys_user" edge to the SysUser entity.
func (_u *CourseEntityUpdate) ClearSysUser() *CourseEntityUpdate {
_u.mutation.ClearSysUser()
return _u
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *CourseEntityUpdate) Save(ctx context.Context) (int, error) {
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *CourseEntityUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *CourseEntityUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *CourseEntityUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *CourseEntityUpdate) check() error {
if v, ok := _u.mutation.GetType(); ok {
if err := courseentity.TypeValidator(v); err != nil {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.type": %w`, err)}
}
}
if v, ok := _u.mutation.Need(); ok {
if err := courseentity.NeedValidator(v); err != nil {
return &ValidationError{Name: "need", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.need": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := courseentity.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.status": %w`, err)}
}
}
if v, ok := _u.mutation.ExamState(); ok {
if err := courseentity.ExamStateValidator(v); err != nil {
return &ValidationError{Name: "exam_state", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.exam_state": %w`, err)}
}
}
if v, ok := _u.mutation.Violence(); ok {
if err := courseentity.ViolenceValidator(v); err != nil {
return &ValidationError{Name: "violence", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.violence": %w`, err)}
}
}
if _u.mutation.SysUserCleared() && len(_u.mutation.SysUserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "CourseEntity.sys_user"`)
}
return nil
}
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
func (_u *CourseEntityUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *CourseEntityUpdate {
_u.modifiers = append(_u.modifiers, modifiers...)
return _u
}
func (_u *CourseEntityUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(courseentity.Table, courseentity.Columns, sqlgraph.NewFieldSpec(courseentity.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdateTime(); ok {
_spec.SetField(courseentity.FieldUpdateTime, field.TypeString, value)
}
if value, ok := _u.mutation.Username(); ok {
_spec.SetField(courseentity.FieldUsername, field.TypeString, value)
}
if value, ok := _u.mutation.Password(); ok {
_spec.SetField(courseentity.FieldPassword, field.TypeString, value)
}
if value, ok := _u.mutation.GetType(); ok {
_spec.SetField(courseentity.FieldType, field.TypeEnum, value)
}
if value, ok := _u.mutation.CourseName(); ok {
_spec.SetField(courseentity.FieldCourseName, field.TypeString, value)
}
if value, ok := _u.mutation.CourseID(); ok {
_spec.SetField(courseentity.FieldCourseID, field.TypeString, value)
}
if value, ok := _u.mutation.Need(); ok {
_spec.SetField(courseentity.FieldNeed, field.TypeEnum, value)
}
if value, ok := _u.mutation.Status(); ok {
_spec.SetField(courseentity.FieldStatus, field.TypeEnum, value)
}
if value, ok := _u.mutation.TaskCount(); ok {
_spec.SetField(courseentity.FieldTaskCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedTaskCount(); ok {
_spec.AddField(courseentity.FieldTaskCount, field.TypeInt, value)
}
if value, ok := _u.mutation.SuccessCount(); ok {
_spec.SetField(courseentity.FieldSuccessCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedSuccessCount(); ok {
_spec.AddField(courseentity.FieldSuccessCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ExamState(); ok {
_spec.SetField(courseentity.FieldExamState, field.TypeEnum, value)
}
if value, ok := _u.mutation.Violence(); ok {
_spec.SetField(courseentity.FieldViolence, field.TypeEnum, value)
}
if value, ok := _u.mutation.Desc(); ok {
_spec.SetField(courseentity.FieldDesc, field.TypeString, value)
}
if _u.mutation.DescCleared() {
_spec.ClearField(courseentity.FieldDesc, field.TypeString)
}
if _u.mutation.SysUserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: courseentity.SysUserTable,
Columns: []string{courseentity.SysUserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(sysuser.FieldID, field.TypeInt64),
},
}
edge.Schema = _u.schemaConfig.CourseEntity
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.SysUserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: courseentity.SysUserTable,
Columns: []string{courseentity.SysUserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(sysuser.FieldID, field.TypeInt64),
},
}
edge.Schema = _u.schemaConfig.CourseEntity
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_spec.Node.Schema = _u.schemaConfig.CourseEntity
ctx = internal.NewSchemaConfigContext(ctx, _u.schemaConfig)
_spec.AddModifiers(_u.modifiers...)
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{courseentity.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// CourseEntityUpdateOne is the builder for updating a single CourseEntity entity.
type CourseEntityUpdateOne struct {
config
fields []string
hooks []Hook
mutation *CourseEntityMutation
modifiers []func(*sql.UpdateBuilder)
}
// SetUpdateTime sets the "update_time" field.
func (_u *CourseEntityUpdateOne) SetUpdateTime(v *ent2.CustomTime) *CourseEntityUpdateOne {
_u.mutation.SetUpdateTime(v)
return _u
}
// SetUserID sets the "user_id" field.
func (_u *CourseEntityUpdateOne) SetUserID(v int64) *CourseEntityUpdateOne {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableUserID(v *int64) *CourseEntityUpdateOne {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetUsername sets the "username" field.
func (_u *CourseEntityUpdateOne) SetUsername(v string) *CourseEntityUpdateOne {
_u.mutation.SetUsername(v)
return _u
}
// SetNillableUsername sets the "username" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableUsername(v *string) *CourseEntityUpdateOne {
if v != nil {
_u.SetUsername(*v)
}
return _u
}
// SetPassword sets the "password" field.
func (_u *CourseEntityUpdateOne) SetPassword(v string) *CourseEntityUpdateOne {
_u.mutation.SetPassword(v)
return _u
}
// SetNillablePassword sets the "password" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillablePassword(v *string) *CourseEntityUpdateOne {
if v != nil {
_u.SetPassword(*v)
}
return _u
}
// SetType sets the "type" field.
func (_u *CourseEntityUpdateOne) SetType(v courseentity.Type) *CourseEntityUpdateOne {
_u.mutation.SetType(v)
return _u
}
// SetNillableType sets the "type" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableType(v *courseentity.Type) *CourseEntityUpdateOne {
if v != nil {
_u.SetType(*v)
}
return _u
}
// SetCourseName sets the "course_name" field.
func (_u *CourseEntityUpdateOne) SetCourseName(v string) *CourseEntityUpdateOne {
_u.mutation.SetCourseName(v)
return _u
}
// SetNillableCourseName sets the "course_name" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableCourseName(v *string) *CourseEntityUpdateOne {
if v != nil {
_u.SetCourseName(*v)
}
return _u
}
// SetCourseID sets the "course_id" field.
func (_u *CourseEntityUpdateOne) SetCourseID(v string) *CourseEntityUpdateOne {
_u.mutation.SetCourseID(v)
return _u
}
// SetNillableCourseID sets the "course_id" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableCourseID(v *string) *CourseEntityUpdateOne {
if v != nil {
_u.SetCourseID(*v)
}
return _u
}
// SetNeed sets the "need" field.
func (_u *CourseEntityUpdateOne) SetNeed(v courseentity.Need) *CourseEntityUpdateOne {
_u.mutation.SetNeed(v)
return _u
}
// SetNillableNeed sets the "need" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableNeed(v *courseentity.Need) *CourseEntityUpdateOne {
if v != nil {
_u.SetNeed(*v)
}
return _u
}
// SetStatus sets the "status" field.
func (_u *CourseEntityUpdateOne) SetStatus(v courseentity.Status) *CourseEntityUpdateOne {
_u.mutation.SetStatus(v)
return _u
}
// SetNillableStatus sets the "status" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableStatus(v *courseentity.Status) *CourseEntityUpdateOne {
if v != nil {
_u.SetStatus(*v)
}
return _u
}
// SetTaskCount sets the "task_count" field.
func (_u *CourseEntityUpdateOne) SetTaskCount(v int) *CourseEntityUpdateOne {
_u.mutation.ResetTaskCount()
_u.mutation.SetTaskCount(v)
return _u
}
// SetNillableTaskCount sets the "task_count" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableTaskCount(v *int) *CourseEntityUpdateOne {
if v != nil {
_u.SetTaskCount(*v)
}
return _u
}
// AddTaskCount adds value to the "task_count" field.
func (_u *CourseEntityUpdateOne) AddTaskCount(v int) *CourseEntityUpdateOne {
_u.mutation.AddTaskCount(v)
return _u
}
// SetSuccessCount sets the "success_count" field.
func (_u *CourseEntityUpdateOne) SetSuccessCount(v int) *CourseEntityUpdateOne {
_u.mutation.ResetSuccessCount()
_u.mutation.SetSuccessCount(v)
return _u
}
// SetNillableSuccessCount sets the "success_count" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableSuccessCount(v *int) *CourseEntityUpdateOne {
if v != nil {
_u.SetSuccessCount(*v)
}
return _u
}
// AddSuccessCount adds value to the "success_count" field.
func (_u *CourseEntityUpdateOne) AddSuccessCount(v int) *CourseEntityUpdateOne {
_u.mutation.AddSuccessCount(v)
return _u
}
// SetExamState sets the "exam_state" field.
func (_u *CourseEntityUpdateOne) SetExamState(v courseentity.ExamState) *CourseEntityUpdateOne {
_u.mutation.SetExamState(v)
return _u
}
// SetNillableExamState sets the "exam_state" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableExamState(v *courseentity.ExamState) *CourseEntityUpdateOne {
if v != nil {
_u.SetExamState(*v)
}
return _u
}
// SetViolence sets the "violence" field.
func (_u *CourseEntityUpdateOne) SetViolence(v courseentity.Violence) *CourseEntityUpdateOne {
_u.mutation.SetViolence(v)
return _u
}
// SetNillableViolence sets the "violence" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableViolence(v *courseentity.Violence) *CourseEntityUpdateOne {
if v != nil {
_u.SetViolence(*v)
}
return _u
}
// SetDesc sets the "desc" field.
func (_u *CourseEntityUpdateOne) SetDesc(v string) *CourseEntityUpdateOne {
_u.mutation.SetDesc(v)
return _u
}
// SetNillableDesc sets the "desc" field if the given value is not nil.
func (_u *CourseEntityUpdateOne) SetNillableDesc(v *string) *CourseEntityUpdateOne {
if v != nil {
_u.SetDesc(*v)
}
return _u
}
// ClearDesc clears the value of the "desc" field.
func (_u *CourseEntityUpdateOne) ClearDesc() *CourseEntityUpdateOne {
_u.mutation.ClearDesc()
return _u
}
// SetSysUserID sets the "sys_user" edge to the SysUser entity by ID.
func (_u *CourseEntityUpdateOne) SetSysUserID(id int64) *CourseEntityUpdateOne {
_u.mutation.SetSysUserID(id)
return _u
}
// SetSysUser sets the "sys_user" edge to the SysUser entity.
func (_u *CourseEntityUpdateOne) SetSysUser(v *SysUser) *CourseEntityUpdateOne {
return _u.SetSysUserID(v.ID)
}
// Mutation returns the CourseEntityMutation object of the builder.
func (_u *CourseEntityUpdateOne) Mutation() *CourseEntityMutation {
return _u.mutation
}
// ClearSysUser clears the "sys_user" edge to the SysUser entity.
func (_u *CourseEntityUpdateOne) ClearSysUser() *CourseEntityUpdateOne {
_u.mutation.ClearSysUser()
return _u
}
// Where appends a list predicates to the CourseEntityUpdate builder.
func (_u *CourseEntityUpdateOne) Where(ps ...predicate.CourseEntity) *CourseEntityUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *CourseEntityUpdateOne) Select(field string, fields ...string) *CourseEntityUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated CourseEntity entity.
func (_u *CourseEntityUpdateOne) Save(ctx context.Context) (*CourseEntity, error) {
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *CourseEntityUpdateOne) SaveX(ctx context.Context) *CourseEntity {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *CourseEntityUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *CourseEntityUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *CourseEntityUpdateOne) check() error {
if v, ok := _u.mutation.GetType(); ok {
if err := courseentity.TypeValidator(v); err != nil {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.type": %w`, err)}
}
}
if v, ok := _u.mutation.Need(); ok {
if err := courseentity.NeedValidator(v); err != nil {
return &ValidationError{Name: "need", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.need": %w`, err)}
}
}
if v, ok := _u.mutation.Status(); ok {
if err := courseentity.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.status": %w`, err)}
}
}
if v, ok := _u.mutation.ExamState(); ok {
if err := courseentity.ExamStateValidator(v); err != nil {
return &ValidationError{Name: "exam_state", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.exam_state": %w`, err)}
}
}
if v, ok := _u.mutation.Violence(); ok {
if err := courseentity.ViolenceValidator(v); err != nil {
return &ValidationError{Name: "violence", err: fmt.Errorf(`ent: validator failed for field "CourseEntity.violence": %w`, err)}
}
}
if _u.mutation.SysUserCleared() && len(_u.mutation.SysUserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "CourseEntity.sys_user"`)
}
return nil
}
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
func (_u *CourseEntityUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *CourseEntityUpdateOne {
_u.modifiers = append(_u.modifiers, modifiers...)
return _u
}
func (_u *CourseEntityUpdateOne) sqlSave(ctx context.Context) (_node *CourseEntity, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(courseentity.Table, courseentity.Columns, sqlgraph.NewFieldSpec(courseentity.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "CourseEntity.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, courseentity.FieldID)
for _, f := range fields {
if !courseentity.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != courseentity.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdateTime(); ok {
_spec.SetField(courseentity.FieldUpdateTime, field.TypeString, value)
}
if value, ok := _u.mutation.Username(); ok {
_spec.SetField(courseentity.FieldUsername, field.TypeString, value)
}
if value, ok := _u.mutation.Password(); ok {
_spec.SetField(courseentity.FieldPassword, field.TypeString, value)
}
if value, ok := _u.mutation.GetType(); ok {
_spec.SetField(courseentity.FieldType, field.TypeEnum, value)
}
if value, ok := _u.mutation.CourseName(); ok {
_spec.SetField(courseentity.FieldCourseName, field.TypeString, value)
}
if value, ok := _u.mutation.CourseID(); ok {
_spec.SetField(courseentity.FieldCourseID, field.TypeString, value)
}
if value, ok := _u.mutation.Need(); ok {
_spec.SetField(courseentity.FieldNeed, field.TypeEnum, value)
}
if value, ok := _u.mutation.Status(); ok {
_spec.SetField(courseentity.FieldStatus, field.TypeEnum, value)
}
if value, ok := _u.mutation.TaskCount(); ok {
_spec.SetField(courseentity.FieldTaskCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedTaskCount(); ok {
_spec.AddField(courseentity.FieldTaskCount, field.TypeInt, value)
}
if value, ok := _u.mutation.SuccessCount(); ok {
_spec.SetField(courseentity.FieldSuccessCount, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedSuccessCount(); ok {
_spec.AddField(courseentity.FieldSuccessCount, field.TypeInt, value)
}
if value, ok := _u.mutation.ExamState(); ok {
_spec.SetField(courseentity.FieldExamState, field.TypeEnum, value)
}
if value, ok := _u.mutation.Violence(); ok {
_spec.SetField(courseentity.FieldViolence, field.TypeEnum, value)
}
if value, ok := _u.mutation.Desc(); ok {
_spec.SetField(courseentity.FieldDesc, field.TypeString, value)
}
if _u.mutation.DescCleared() {
_spec.ClearField(courseentity.FieldDesc, field.TypeString)
}
if _u.mutation.SysUserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: courseentity.SysUserTable,
Columns: []string{courseentity.SysUserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(sysuser.FieldID, field.TypeInt64),
},
}
edge.Schema = _u.schemaConfig.CourseEntity
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.SysUserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: courseentity.SysUserTable,
Columns: []string{courseentity.SysUserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(sysuser.FieldID, field.TypeInt64),
},
}
edge.Schema = _u.schemaConfig.CourseEntity
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_spec.Node.Schema = _u.schemaConfig.CourseEntity
ctx = internal.NewSchemaConfigContext(ctx, _u.schemaConfig)
_spec.AddModifiers(_u.modifiers...)
_node = &CourseEntity{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{courseentity.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}
+12 -1
View File
@@ -39,6 +39,8 @@ type CpTask struct {
SuccessCount int `json:"success_count,omitempty"`
// FormData holds the value of the "form_data" field.
FormData string `json:"form_data,omitempty"`
// UsedImg holds the value of the "used_img" field.
UsedImg string `json:"used_img,omitempty"`
// Step holds the value of the "step" field.
Step cptask.Step `json:"step,omitempty"`
// NotificationType holds the value of the "notification_type" field.
@@ -109,7 +111,7 @@ func (*CpTask) scanValues(columns []string) ([]any, error) {
values[i] = new(ent2.CustomTime)
case cptask.FieldID, cptask.FieldCpUserID, cptask.FieldUserID, cptask.FieldFrequency, cptask.FieldSuccessCount, cptask.FieldExecuteHour, cptask.FieldExecuteMinute, cptask.FieldNextExecuteTime:
values[i] = new(sql.NullInt64)
case cptask.FieldTaskType, cptask.FieldTitle, cptask.FieldTaskStatus, cptask.FieldFormData, cptask.FieldStep, cptask.FieldNotificationType:
case cptask.FieldTaskType, cptask.FieldTitle, cptask.FieldTaskStatus, cptask.FieldFormData, cptask.FieldUsedImg, cptask.FieldStep, cptask.FieldNotificationType:
values[i] = new(sql.NullString)
default:
values[i] = new(sql.UnknownType)
@@ -192,6 +194,12 @@ func (_m *CpTask) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.FormData = value.String
}
case cptask.FieldUsedImg:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field used_img", values[i])
} else if value.Valid {
_m.UsedImg = value.String
}
case cptask.FieldStep:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field step", values[i])
@@ -306,6 +314,9 @@ func (_m *CpTask) String() string {
builder.WriteString("form_data=")
builder.WriteString(_m.FormData)
builder.WriteString(", ")
builder.WriteString("used_img=")
builder.WriteString(_m.UsedImg)
builder.WriteString(", ")
builder.WriteString("step=")
builder.WriteString(fmt.Sprintf("%v", _m.Step))
builder.WriteString(", ")
+8
View File
@@ -36,6 +36,8 @@ const (
FieldSuccessCount = "success_count"
// FieldFormData holds the string denoting the form_data field in the database.
FieldFormData = "form_data"
// FieldUsedImg holds the string denoting the used_img field in the database.
FieldUsedImg = "used_img"
// FieldStep holds the string denoting the step field in the database.
FieldStep = "step"
// FieldNotificationType holds the string denoting the notification_type field in the database.
@@ -90,6 +92,7 @@ var Columns = []string{
FieldTaskStatus,
FieldSuccessCount,
FieldFormData,
FieldUsedImg,
FieldStep,
FieldNotificationType,
FieldExecuteHour,
@@ -284,6 +287,11 @@ func ByFormData(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldFormData, opts...).ToFunc()
}
// ByUsedImg orders the results by the used_img field.
func ByUsedImg(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUsedImg, opts...).ToFunc()
}
// ByStep orders the results by the step field.
func ByStep(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldStep, opts...).ToFunc()
+80
View File
@@ -96,6 +96,11 @@ func FormData(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldEQ(FieldFormData, v))
}
// UsedImg applies equality check predicate on the "used_img" field. It's identical to UsedImgEQ.
func UsedImg(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldEQ(FieldUsedImg, v))
}
// ExecuteHour applies equality check predicate on the "execute_hour" field. It's identical to ExecuteHourEQ.
func ExecuteHour(v int) predicate.CpTask {
return predicate.CpTask(sql.FieldEQ(FieldExecuteHour, v))
@@ -551,6 +556,81 @@ func FormDataContainsFold(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldContainsFold(FieldFormData, v))
}
// UsedImgEQ applies the EQ predicate on the "used_img" field.
func UsedImgEQ(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldEQ(FieldUsedImg, v))
}
// UsedImgNEQ applies the NEQ predicate on the "used_img" field.
func UsedImgNEQ(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldNEQ(FieldUsedImg, v))
}
// UsedImgIn applies the In predicate on the "used_img" field.
func UsedImgIn(vs ...string) predicate.CpTask {
return predicate.CpTask(sql.FieldIn(FieldUsedImg, vs...))
}
// UsedImgNotIn applies the NotIn predicate on the "used_img" field.
func UsedImgNotIn(vs ...string) predicate.CpTask {
return predicate.CpTask(sql.FieldNotIn(FieldUsedImg, vs...))
}
// UsedImgGT applies the GT predicate on the "used_img" field.
func UsedImgGT(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldGT(FieldUsedImg, v))
}
// UsedImgGTE applies the GTE predicate on the "used_img" field.
func UsedImgGTE(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldGTE(FieldUsedImg, v))
}
// UsedImgLT applies the LT predicate on the "used_img" field.
func UsedImgLT(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldLT(FieldUsedImg, v))
}
// UsedImgLTE applies the LTE predicate on the "used_img" field.
func UsedImgLTE(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldLTE(FieldUsedImg, v))
}
// UsedImgContains applies the Contains predicate on the "used_img" field.
func UsedImgContains(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldContains(FieldUsedImg, v))
}
// UsedImgHasPrefix applies the HasPrefix predicate on the "used_img" field.
func UsedImgHasPrefix(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldHasPrefix(FieldUsedImg, v))
}
// UsedImgHasSuffix applies the HasSuffix predicate on the "used_img" field.
func UsedImgHasSuffix(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldHasSuffix(FieldUsedImg, v))
}
// UsedImgIsNil applies the IsNil predicate on the "used_img" field.
func UsedImgIsNil() predicate.CpTask {
return predicate.CpTask(sql.FieldIsNull(FieldUsedImg))
}
// UsedImgNotNil applies the NotNil predicate on the "used_img" field.
func UsedImgNotNil() predicate.CpTask {
return predicate.CpTask(sql.FieldNotNull(FieldUsedImg))
}
// UsedImgEqualFold applies the EqualFold predicate on the "used_img" field.
func UsedImgEqualFold(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldEqualFold(FieldUsedImg, v))
}
// UsedImgContainsFold applies the ContainsFold predicate on the "used_img" field.
func UsedImgContainsFold(v string) predicate.CpTask {
return predicate.CpTask(sql.FieldContainsFold(FieldUsedImg, v))
}
// StepEQ applies the EQ predicate on the "step" field.
func StepEQ(v Step) predicate.CpTask {
return predicate.CpTask(sql.FieldEQ(FieldStep, v))
+78
View File
@@ -109,6 +109,20 @@ func (_c *CpTaskCreate) SetNillableFormData(v *string) *CpTaskCreate {
return _c
}
// SetUsedImg sets the "used_img" field.
func (_c *CpTaskCreate) SetUsedImg(v string) *CpTaskCreate {
_c.mutation.SetUsedImg(v)
return _c
}
// SetNillableUsedImg sets the "used_img" field if the given value is not nil.
func (_c *CpTaskCreate) SetNillableUsedImg(v *string) *CpTaskCreate {
if v != nil {
_c.SetUsedImg(*v)
}
return _c
}
// SetStep sets the "step" field.
func (_c *CpTaskCreate) SetStep(v cptask.Step) *CpTaskCreate {
_c.mutation.SetStep(v)
@@ -382,6 +396,10 @@ func (_c *CpTaskCreate) createSpec() (*CpTask, *sqlgraph.CreateSpec) {
_spec.SetField(cptask.FieldFormData, field.TypeString, value)
_node.FormData = value
}
if value, ok := _c.mutation.UsedImg(); ok {
_spec.SetField(cptask.FieldUsedImg, field.TypeString, value)
_node.UsedImg = value
}
if value, ok := _c.mutation.Step(); ok {
_spec.SetField(cptask.FieldStep, field.TypeEnum, value)
_node.Step = value
@@ -633,6 +651,24 @@ func (u *CpTaskUpsert) ClearFormData() *CpTaskUpsert {
return u
}
// SetUsedImg sets the "used_img" field.
func (u *CpTaskUpsert) SetUsedImg(v string) *CpTaskUpsert {
u.Set(cptask.FieldUsedImg, v)
return u
}
// UpdateUsedImg sets the "used_img" field to the value that was provided on create.
func (u *CpTaskUpsert) UpdateUsedImg() *CpTaskUpsert {
u.SetExcluded(cptask.FieldUsedImg)
return u
}
// ClearUsedImg clears the value of the "used_img" field.
func (u *CpTaskUpsert) ClearUsedImg() *CpTaskUpsert {
u.SetNull(cptask.FieldUsedImg)
return u
}
// SetStep sets the "step" field.
func (u *CpTaskUpsert) SetStep(v cptask.Step) *CpTaskUpsert {
u.Set(cptask.FieldStep, v)
@@ -921,6 +957,27 @@ func (u *CpTaskUpsertOne) ClearFormData() *CpTaskUpsertOne {
})
}
// SetUsedImg sets the "used_img" field.
func (u *CpTaskUpsertOne) SetUsedImg(v string) *CpTaskUpsertOne {
return u.Update(func(s *CpTaskUpsert) {
s.SetUsedImg(v)
})
}
// UpdateUsedImg sets the "used_img" field to the value that was provided on create.
func (u *CpTaskUpsertOne) UpdateUsedImg() *CpTaskUpsertOne {
return u.Update(func(s *CpTaskUpsert) {
s.UpdateUsedImg()
})
}
// ClearUsedImg clears the value of the "used_img" field.
func (u *CpTaskUpsertOne) ClearUsedImg() *CpTaskUpsertOne {
return u.Update(func(s *CpTaskUpsert) {
s.ClearUsedImg()
})
}
// SetStep sets the "step" field.
func (u *CpTaskUpsertOne) SetStep(v cptask.Step) *CpTaskUpsertOne {
return u.Update(func(s *CpTaskUpsert) {
@@ -1391,6 +1448,27 @@ func (u *CpTaskUpsertBulk) ClearFormData() *CpTaskUpsertBulk {
})
}
// SetUsedImg sets the "used_img" field.
func (u *CpTaskUpsertBulk) SetUsedImg(v string) *CpTaskUpsertBulk {
return u.Update(func(s *CpTaskUpsert) {
s.SetUsedImg(v)
})
}
// UpdateUsedImg sets the "used_img" field to the value that was provided on create.
func (u *CpTaskUpsertBulk) UpdateUsedImg() *CpTaskUpsertBulk {
return u.Update(func(s *CpTaskUpsert) {
s.UpdateUsedImg()
})
}
// ClearUsedImg clears the value of the "used_img" field.
func (u *CpTaskUpsertBulk) ClearUsedImg() *CpTaskUpsertBulk {
return u.Update(func(s *CpTaskUpsert) {
s.ClearUsedImg()
})
}
// SetStep sets the "step" field.
func (u *CpTaskUpsertBulk) SetStep(v cptask.Step) *CpTaskUpsertBulk {
return u.Update(func(s *CpTaskUpsert) {
+52
View File
@@ -171,6 +171,26 @@ func (_u *CpTaskUpdate) ClearFormData() *CpTaskUpdate {
return _u
}
// SetUsedImg sets the "used_img" field.
func (_u *CpTaskUpdate) SetUsedImg(v string) *CpTaskUpdate {
_u.mutation.SetUsedImg(v)
return _u
}
// SetNillableUsedImg sets the "used_img" field if the given value is not nil.
func (_u *CpTaskUpdate) SetNillableUsedImg(v *string) *CpTaskUpdate {
if v != nil {
_u.SetUsedImg(*v)
}
return _u
}
// ClearUsedImg clears the value of the "used_img" field.
func (_u *CpTaskUpdate) ClearUsedImg() *CpTaskUpdate {
_u.mutation.ClearUsedImg()
return _u
}
// SetStep sets the "step" field.
func (_u *CpTaskUpdate) SetStep(v cptask.Step) *CpTaskUpdate {
_u.mutation.SetStep(v)
@@ -460,6 +480,12 @@ func (_u *CpTaskUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if _u.mutation.FormDataCleared() {
_spec.ClearField(cptask.FieldFormData, field.TypeString)
}
if value, ok := _u.mutation.UsedImg(); ok {
_spec.SetField(cptask.FieldUsedImg, field.TypeString, value)
}
if _u.mutation.UsedImgCleared() {
_spec.ClearField(cptask.FieldUsedImg, field.TypeString)
}
if value, ok := _u.mutation.Step(); ok {
_spec.SetField(cptask.FieldStep, field.TypeEnum, value)
}
@@ -765,6 +791,26 @@ func (_u *CpTaskUpdateOne) ClearFormData() *CpTaskUpdateOne {
return _u
}
// SetUsedImg sets the "used_img" field.
func (_u *CpTaskUpdateOne) SetUsedImg(v string) *CpTaskUpdateOne {
_u.mutation.SetUsedImg(v)
return _u
}
// SetNillableUsedImg sets the "used_img" field if the given value is not nil.
func (_u *CpTaskUpdateOne) SetNillableUsedImg(v *string) *CpTaskUpdateOne {
if v != nil {
_u.SetUsedImg(*v)
}
return _u
}
// ClearUsedImg clears the value of the "used_img" field.
func (_u *CpTaskUpdateOne) ClearUsedImg() *CpTaskUpdateOne {
_u.mutation.ClearUsedImg()
return _u
}
// SetStep sets the "step" field.
func (_u *CpTaskUpdateOne) SetStep(v cptask.Step) *CpTaskUpdateOne {
_u.mutation.SetStep(v)
@@ -1084,6 +1130,12 @@ func (_u *CpTaskUpdateOne) sqlSave(ctx context.Context) (_node *CpTask, err erro
if _u.mutation.FormDataCleared() {
_spec.ClearField(cptask.FieldFormData, field.TypeString)
}
if value, ok := _u.mutation.UsedImg(); ok {
_spec.SetField(cptask.FieldUsedImg, field.TypeString, value)
}
if _u.mutation.UsedImgCleared() {
_spec.ClearField(cptask.FieldUsedImg, field.TypeString)
}
if value, ok := _u.mutation.Step(); ok {
_spec.SetField(cptask.FieldStep, field.TypeEnum, value)
}
+13 -11
View File
@@ -6,6 +6,7 @@ import (
"context"
"errors"
"fmt"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/cpimage"
"likedream-goframe/internal/ent/cpschool"
"likedream-goframe/internal/ent/cptask"
@@ -83,17 +84,18 @@ var (
func checkColumn(t, c string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
cpimage.Table: cpimage.ValidColumn,
cpschool.Table: cpschool.ValidColumn,
cptask.Table: cptask.ValidColumn,
cptasklog.Table: cptasklog.ValidColumn,
cpuser.Table: cpuser.ValidColumn,
sysagent.Table: sysagent.ValidColumn,
syscard.Table: syscard.ValidColumn,
sysmenu.Table: sysmenu.ValidColumn,
sysrole.Table: sysrole.ValidColumn,
sysuser.Table: sysuser.ValidColumn,
sysuserlog.Table: sysuserlog.ValidColumn,
courseentity.Table: courseentity.ValidColumn,
cpimage.Table: cpimage.ValidColumn,
cpschool.Table: cpschool.ValidColumn,
cptask.Table: cptask.ValidColumn,
cptasklog.Table: cptasklog.ValidColumn,
cpuser.Table: cpuser.ValidColumn,
sysagent.Table: sysagent.ValidColumn,
syscard.Table: syscard.ValidColumn,
sysmenu.Table: sysmenu.ValidColumn,
sysrole.Table: sysrole.ValidColumn,
sysuser.Table: sysuser.ValidColumn,
sysuserlog.Table: sysuserlog.ValidColumn,
})
})
return columnCheck(t, c)
+224 -22
View File
@@ -3,6 +3,7 @@
package ent
import (
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/cpimage"
"likedream-goframe/internal/ent/cpschool"
"likedream-goframe/internal/ent/cptask"
@@ -24,8 +25,36 @@ import (
// schemaGraph holds a representation of ent/schema at runtime.
var schemaGraph = func() *sqlgraph.Schema {
graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, 11)}
graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, 12)}
graph.Nodes[0] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: courseentity.Table,
Columns: courseentity.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt64,
Column: courseentity.FieldID,
},
},
Type: "CourseEntity",
Fields: map[string]*sqlgraph.FieldSpec{
courseentity.FieldCreateTime: {Type: field.TypeString, Column: courseentity.FieldCreateTime},
courseentity.FieldUpdateTime: {Type: field.TypeString, Column: courseentity.FieldUpdateTime},
courseentity.FieldUserID: {Type: field.TypeInt64, Column: courseentity.FieldUserID},
courseentity.FieldUsername: {Type: field.TypeString, Column: courseentity.FieldUsername},
courseentity.FieldPassword: {Type: field.TypeString, Column: courseentity.FieldPassword},
courseentity.FieldType: {Type: field.TypeEnum, Column: courseentity.FieldType},
courseentity.FieldCourseName: {Type: field.TypeString, Column: courseentity.FieldCourseName},
courseentity.FieldCourseID: {Type: field.TypeString, Column: courseentity.FieldCourseID},
courseentity.FieldNeed: {Type: field.TypeEnum, Column: courseentity.FieldNeed},
courseentity.FieldStatus: {Type: field.TypeEnum, Column: courseentity.FieldStatus},
courseentity.FieldTaskCount: {Type: field.TypeInt, Column: courseentity.FieldTaskCount},
courseentity.FieldSuccessCount: {Type: field.TypeInt, Column: courseentity.FieldSuccessCount},
courseentity.FieldExamState: {Type: field.TypeEnum, Column: courseentity.FieldExamState},
courseentity.FieldViolence: {Type: field.TypeEnum, Column: courseentity.FieldViolence},
courseentity.FieldDesc: {Type: field.TypeString, Column: courseentity.FieldDesc},
},
}
graph.Nodes[1] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: cpimage.Table,
Columns: cpimage.Columns,
@@ -45,7 +74,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cpimage.FieldContentLength: {Type: field.TypeInt64, Column: cpimage.FieldContentLength},
},
}
graph.Nodes[1] = &sqlgraph.Node{
graph.Nodes[2] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: cpschool.Table,
Columns: cpschool.Columns,
@@ -63,7 +92,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cpschool.FieldIconImg: {Type: field.TypeString, Column: cpschool.FieldIconImg},
},
}
graph.Nodes[2] = &sqlgraph.Node{
graph.Nodes[3] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: cptask.Table,
Columns: cptask.Columns,
@@ -84,6 +113,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cptask.FieldTaskStatus: {Type: field.TypeEnum, Column: cptask.FieldTaskStatus},
cptask.FieldSuccessCount: {Type: field.TypeInt, Column: cptask.FieldSuccessCount},
cptask.FieldFormData: {Type: field.TypeString, Column: cptask.FieldFormData},
cptask.FieldUsedImg: {Type: field.TypeString, Column: cptask.FieldUsedImg},
cptask.FieldStep: {Type: field.TypeEnum, Column: cptask.FieldStep},
cptask.FieldNotificationType: {Type: field.TypeEnum, Column: cptask.FieldNotificationType},
cptask.FieldExecuteHour: {Type: field.TypeInt, Column: cptask.FieldExecuteHour},
@@ -91,7 +121,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cptask.FieldNextExecuteTime: {Type: field.TypeInt64, Column: cptask.FieldNextExecuteTime},
},
}
graph.Nodes[3] = &sqlgraph.Node{
graph.Nodes[4] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: cptasklog.Table,
Columns: cptasklog.Columns,
@@ -112,7 +142,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cptasklog.FieldRealName: {Type: field.TypeString, Column: cptasklog.FieldRealName},
},
}
graph.Nodes[4] = &sqlgraph.Node{
graph.Nodes[5] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: cpuser.Table,
Columns: cpuser.Columns,
@@ -135,7 +165,7 @@ var schemaGraph = func() *sqlgraph.Schema {
cpuser.FieldMobileType: {Type: field.TypeEnum, Column: cpuser.FieldMobileType},
},
}
graph.Nodes[5] = &sqlgraph.Node{
graph.Nodes[6] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: sysagent.Table,
Columns: sysagent.Columns,
@@ -152,7 +182,7 @@ var schemaGraph = func() *sqlgraph.Schema {
sysagent.FieldRate: {Type: field.TypeString, Column: sysagent.FieldRate},
},
}
graph.Nodes[6] = &sqlgraph.Node{
graph.Nodes[7] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: syscard.Table,
Columns: syscard.Columns,
@@ -174,7 +204,7 @@ var schemaGraph = func() *sqlgraph.Schema {
syscard.FieldCashValue: {Type: field.TypeString, Column: syscard.FieldCashValue},
},
}
graph.Nodes[7] = &sqlgraph.Node{
graph.Nodes[8] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: sysmenu.Table,
Columns: sysmenu.Columns,
@@ -200,7 +230,7 @@ var schemaGraph = func() *sqlgraph.Schema {
sysmenu.FieldOrder: {Type: field.TypeUint, Column: sysmenu.FieldOrder},
},
}
graph.Nodes[8] = &sqlgraph.Node{
graph.Nodes[9] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: sysrole.Table,
Columns: sysrole.Columns,
@@ -219,7 +249,7 @@ var schemaGraph = func() *sqlgraph.Schema {
sysrole.FieldOrder: {Type: field.TypeUint, Column: sysrole.FieldOrder},
},
}
graph.Nodes[9] = &sqlgraph.Node{
graph.Nodes[10] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: sysuser.Table,
Columns: sysuser.Columns,
@@ -242,7 +272,7 @@ var schemaGraph = func() *sqlgraph.Schema {
sysuser.FieldInviteCashback: {Type: field.TypeString, Column: sysuser.FieldInviteCashback},
},
}
graph.Nodes[10] = &sqlgraph.Node{
graph.Nodes[11] = &sqlgraph.Node{
NodeSpec: sqlgraph.NodeSpec{
Table: sysuserlog.Table,
Columns: sysuserlog.Columns,
@@ -254,6 +284,18 @@ var schemaGraph = func() *sqlgraph.Schema {
Type: "SysUserLog",
Fields: map[string]*sqlgraph.FieldSpec{},
}
graph.MustAddE(
"sys_user",
&sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: courseentity.SysUserTable,
Columns: []string{courseentity.SysUserColumn},
Bidi: false,
},
"CourseEntity",
"SysUser",
)
graph.MustAddE(
"user",
&sqlgraph.EdgeSpec{
@@ -590,6 +632,18 @@ var schemaGraph = func() *sqlgraph.Schema {
"SysUser",
"CpTask",
)
graph.MustAddE(
"course_entity",
&sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: sysuser.CourseEntityTable,
Columns: []string{sysuser.CourseEntityColumn},
Bidi: false,
},
"SysUser",
"CourseEntity",
)
graph.MustAddE(
"cp_task_logs",
&sqlgraph.EdgeSpec{
@@ -647,6 +701,135 @@ type predicateAdder interface {
addPredicate(func(s *sql.Selector))
}
// addPredicate implements the predicateAdder interface.
func (_q *CourseEntityQuery) addPredicate(pred func(s *sql.Selector)) {
_q.predicates = append(_q.predicates, pred)
}
// Filter returns a Filter implementation to apply filters on the CourseEntityQuery builder.
func (_q *CourseEntityQuery) Filter() *CourseEntityFilter {
return &CourseEntityFilter{config: _q.config, predicateAdder: _q}
}
// addPredicate implements the predicateAdder interface.
func (m *CourseEntityMutation) addPredicate(pred func(s *sql.Selector)) {
m.predicates = append(m.predicates, pred)
}
// Filter returns an entql.Where implementation to apply filters on the CourseEntityMutation builder.
func (m *CourseEntityMutation) Filter() *CourseEntityFilter {
return &CourseEntityFilter{config: m.config, predicateAdder: m}
}
// CourseEntityFilter provides a generic filtering capability at runtime for CourseEntityQuery.
type CourseEntityFilter struct {
predicateAdder
config
}
// Where applies the entql predicate on the query filter.
func (f *CourseEntityFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[0].Type, p, s); err != nil {
s.AddError(err)
}
})
}
// WhereID applies the entql int64 predicate on the id field.
func (f *CourseEntityFilter) WhereID(p entql.Int64P) {
f.Where(p.Field(courseentity.FieldID))
}
// WhereCreateTime applies the entql string predicate on the create_time field.
func (f *CourseEntityFilter) WhereCreateTime(p entql.StringP) {
f.Where(p.Field(courseentity.FieldCreateTime))
}
// WhereUpdateTime applies the entql string predicate on the update_time field.
func (f *CourseEntityFilter) WhereUpdateTime(p entql.StringP) {
f.Where(p.Field(courseentity.FieldUpdateTime))
}
// WhereUserID applies the entql int64 predicate on the user_id field.
func (f *CourseEntityFilter) WhereUserID(p entql.Int64P) {
f.Where(p.Field(courseentity.FieldUserID))
}
// WhereUsername applies the entql string predicate on the username field.
func (f *CourseEntityFilter) WhereUsername(p entql.StringP) {
f.Where(p.Field(courseentity.FieldUsername))
}
// WherePassword applies the entql string predicate on the password field.
func (f *CourseEntityFilter) WherePassword(p entql.StringP) {
f.Where(p.Field(courseentity.FieldPassword))
}
// WhereType applies the entql string predicate on the type field.
func (f *CourseEntityFilter) WhereType(p entql.StringP) {
f.Where(p.Field(courseentity.FieldType))
}
// WhereCourseName applies the entql string predicate on the course_name field.
func (f *CourseEntityFilter) WhereCourseName(p entql.StringP) {
f.Where(p.Field(courseentity.FieldCourseName))
}
// WhereCourseID applies the entql string predicate on the course_id field.
func (f *CourseEntityFilter) WhereCourseID(p entql.StringP) {
f.Where(p.Field(courseentity.FieldCourseID))
}
// WhereNeed applies the entql string predicate on the need field.
func (f *CourseEntityFilter) WhereNeed(p entql.StringP) {
f.Where(p.Field(courseentity.FieldNeed))
}
// WhereStatus applies the entql string predicate on the status field.
func (f *CourseEntityFilter) WhereStatus(p entql.StringP) {
f.Where(p.Field(courseentity.FieldStatus))
}
// WhereTaskCount applies the entql int predicate on the task_count field.
func (f *CourseEntityFilter) WhereTaskCount(p entql.IntP) {
f.Where(p.Field(courseentity.FieldTaskCount))
}
// WhereSuccessCount applies the entql int predicate on the success_count field.
func (f *CourseEntityFilter) WhereSuccessCount(p entql.IntP) {
f.Where(p.Field(courseentity.FieldSuccessCount))
}
// WhereExamState applies the entql string predicate on the exam_state field.
func (f *CourseEntityFilter) WhereExamState(p entql.StringP) {
f.Where(p.Field(courseentity.FieldExamState))
}
// WhereViolence applies the entql string predicate on the violence field.
func (f *CourseEntityFilter) WhereViolence(p entql.StringP) {
f.Where(p.Field(courseentity.FieldViolence))
}
// WhereDesc applies the entql string predicate on the desc field.
func (f *CourseEntityFilter) WhereDesc(p entql.StringP) {
f.Where(p.Field(courseentity.FieldDesc))
}
// WhereHasSysUser applies a predicate to check if query has an edge sys_user.
func (f *CourseEntityFilter) WhereHasSysUser() {
f.Where(entql.HasEdge("sys_user"))
}
// WhereHasSysUserWith applies a predicate to check if query has an edge sys_user with a given conditions (other predicates).
func (f *CourseEntityFilter) WhereHasSysUserWith(preds ...predicate.SysUser) {
f.Where(entql.HasEdgeWith("sys_user", sqlgraph.WrapFunc(func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})))
}
// addPredicate implements the predicateAdder interface.
func (_q *CpImageQuery) addPredicate(pred func(s *sql.Selector)) {
_q.predicates = append(_q.predicates, pred)
@@ -676,7 +859,7 @@ type CpImageFilter struct {
// Where applies the entql predicate on the query filter.
func (f *CpImageFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[0].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[1].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -765,7 +948,7 @@ type CpSchoolFilter struct {
// Where applies the entql predicate on the query filter.
func (f *CpSchoolFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[1].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[2].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -844,7 +1027,7 @@ type CpTaskFilter struct {
// Where applies the entql predicate on the query filter.
func (f *CpTaskFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[2].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[3].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -905,6 +1088,11 @@ func (f *CpTaskFilter) WhereFormData(p entql.StringP) {
f.Where(p.Field(cptask.FieldFormData))
}
// WhereUsedImg applies the entql string predicate on the used_img field.
func (f *CpTaskFilter) WhereUsedImg(p entql.StringP) {
f.Where(p.Field(cptask.FieldUsedImg))
}
// WhereStep applies the entql string predicate on the step field.
func (f *CpTaskFilter) WhereStep(p entql.StringP) {
f.Where(p.Field(cptask.FieldStep))
@@ -1001,7 +1189,7 @@ type CpTaskLogFilter struct {
// Where applies the entql predicate on the query filter.
func (f *CpTaskLogFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[3].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[4].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1123,7 +1311,7 @@ type CpUserFilter struct {
// Where applies the entql predicate on the query filter.
func (f *CpUserFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[4].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[5].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1269,7 +1457,7 @@ type SysAgentFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysAgentFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[5].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[6].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1343,7 +1531,7 @@ type SysCardFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysCardFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[6].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[7].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1470,7 +1658,7 @@ type SysMenuFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysMenuFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[7].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[8].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1617,7 +1805,7 @@ type SysRoleFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysRoleFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[8].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[9].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1715,7 +1903,7 @@ type SysUserFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysUserFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[9].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[10].Type, p, s); err != nil {
s.AddError(err)
}
})
@@ -1874,6 +2062,20 @@ func (f *SysUserFilter) WhereHasCpTaskWith(preds ...predicate.CpTask) {
})))
}
// WhereHasCourseEntity applies a predicate to check if query has an edge course_entity.
func (f *SysUserFilter) WhereHasCourseEntity() {
f.Where(entql.HasEdge("course_entity"))
}
// WhereHasCourseEntityWith applies a predicate to check if query has an edge course_entity with a given conditions (other predicates).
func (f *SysUserFilter) WhereHasCourseEntityWith(preds ...predicate.CourseEntity) {
f.Where(entql.HasEdgeWith("course_entity", sqlgraph.WrapFunc(func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})))
}
// WhereHasCpTaskLogs applies a predicate to check if query has an edge cp_task_logs.
func (f *SysUserFilter) WhereHasCpTaskLogs() {
f.Where(entql.HasEdge("cp_task_logs"))
@@ -1959,7 +2161,7 @@ type SysUserLogFilter struct {
// Where applies the entql predicate on the query filter.
func (f *SysUserLogFilter) Where(p entql.P) {
f.addPredicate(func(s *sql.Selector) {
if err := schemaGraph.EvalP(schemaGraph.Nodes[10].Type, p, s); err != nil {
if err := schemaGraph.EvalP(schemaGraph.Nodes[11].Type, p, s); err != nil {
s.AddError(err)
}
})
+12
View File
@@ -8,6 +8,18 @@ import (
"likedream-goframe/internal/ent"
)
// The CourseEntityFunc type is an adapter to allow the use of ordinary
// function as CourseEntity mutator.
type CourseEntityFunc func(context.Context, *ent.CourseEntityMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f CourseEntityFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.CourseEntityMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CourseEntityMutation", m)
}
// The CpImageFunc type is an adapter to allow the use of ordinary
// function as CpImage mutator.
type CpImageFunc func(context.Context, *ent.CpImageMutation) (ent.Value, error)
+30
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/ent/courseentity"
"likedream-goframe/internal/ent/cpimage"
"likedream-goframe/internal/ent/cpschool"
"likedream-goframe/internal/ent/cptask"
@@ -79,6 +80,33 @@ func (f TraverseFunc) Traverse(ctx context.Context, q ent.Query) error {
return f(ctx, query)
}
// The CourseEntityFunc type is an adapter to allow the use of ordinary function as a Querier.
type CourseEntityFunc func(context.Context, *ent.CourseEntityQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f CourseEntityFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.CourseEntityQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.CourseEntityQuery", q)
}
// The TraverseCourseEntity type is an adapter to allow the use of ordinary function as Traverser.
type TraverseCourseEntity func(context.Context, *ent.CourseEntityQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseCourseEntity) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseCourseEntity) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.CourseEntityQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.CourseEntityQuery", q)
}
// The CpImageFunc type is an adapter to allow the use of ordinary function as a Querier.
type CpImageFunc func(context.Context, *ent.CpImageQuery) (ent.Value, error)
@@ -379,6 +407,8 @@ func (f TraverseSysUserLog) Traverse(ctx context.Context, q ent.Query) error {
// NewQuery returns the generic Query interface for the given typed query.
func NewQuery(q ent.Query) (Query, error) {
switch q := q.(type) {
case *ent.CourseEntityQuery:
return &query[*ent.CourseEntityQuery, predicate.CourseEntity, courseentity.OrderOption]{typ: ent.TypeCourseEntity, tq: q}, nil
case *ent.CpImageQuery:
return &query[*ent.CpImageQuery, predicate.CpImage, cpimage.OrderOption]{typ: ent.TypeCpImage, tq: q}, nil
case *ent.CpSchoolQuery:
File diff suppressed because one or more lines are too long
+1
View File
@@ -7,6 +7,7 @@ import "context"
// SchemaConfig represents alternative schema names for all tables
// that can be passed at runtime.
type SchemaConfig struct {
CourseEntity string // CourseEntity table.
CpImage string // CpImage table.
CpSchool string // CpSchool table.
CpTask string // CpTask table.
+42 -3
View File
@@ -9,6 +9,39 @@ import (
)
var (
// CourseEntityColumns holds the columns for the "course_entity" table.
CourseEntityColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "create_time", Type: field.TypeString, SchemaType: map[string]string{"mysql": "datetime", "postgres": "timestamp", "sqlite3": "datetime"}},
{Name: "update_time", Type: field.TypeString, SchemaType: map[string]string{"mysql": "datetime", "postgres": "timestamp", "sqlite3": "datetime"}},
{Name: "username", Type: field.TypeString},
{Name: "password", Type: field.TypeString},
{Name: "type", Type: field.TypeEnum, Enums: []string{"xxt"}},
{Name: "course_name", Type: field.TypeString},
{Name: "course_id", Type: field.TypeString},
{Name: "need", Type: field.TypeEnum, Enums: []string{"task", "exam", "all"}},
{Name: "status", Type: field.TypeEnum, Enums: []string{"not_start", "waiting", "doing", "done", "fail", "pause"}, Default: "not_start"},
{Name: "task_count", Type: field.TypeInt, Default: 0},
{Name: "success_count", Type: field.TypeInt, Default: 0},
{Name: "exam_state", Type: field.TypeEnum, Enums: []string{"not_start", "waiting", "doing", "done", "fail"}, Default: "not_start"},
{Name: "violence", Type: field.TypeEnum, Enums: []string{"no", "yes"}, Default: "no"},
{Name: "desc", Type: field.TypeString, Nullable: true},
{Name: "user_id", Type: field.TypeInt64},
}
// CourseEntityTable holds the schema information for the "course_entity" table.
CourseEntityTable = &schema.Table{
Name: "course_entity",
Columns: CourseEntityColumns,
PrimaryKey: []*schema.Column{CourseEntityColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "course_entity_sys_user_course_entity",
Columns: []*schema.Column{CourseEntityColumns[15]},
RefColumns: []*schema.Column{SysUserColumns[0]},
OnDelete: schema.Cascade,
},
},
}
// CpImageColumns holds the columns for the "cp_image" table.
CpImageColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
@@ -60,6 +93,7 @@ var (
{Name: "task_status", Type: field.TypeEnum, Enums: []string{"not_start", "doing", "done", "fail", "pause", "queue", "unknown"}},
{Name: "success_count", Type: field.TypeInt, Default: 0},
{Name: "form_data", Type: field.TypeString, Nullable: true, Size: 2147483647},
{Name: "used_img", Type: field.TypeString, Nullable: true},
{Name: "step", Type: field.TypeEnum, Enums: []string{"form", "execute", "complete"}},
{Name: "notification_type", Type: field.TypeEnum, Enums: []string{"none", "cp_user_email", "system_user_email"}},
{Name: "execute_hour", Type: field.TypeInt, Nullable: true},
@@ -76,13 +110,13 @@ var (
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "cp_tasks_cp_users_cptasks",
Columns: []*schema.Column{CpTasksColumns[14]},
Columns: []*schema.Column{CpTasksColumns[15]},
RefColumns: []*schema.Column{CpUsersColumns[0]},
OnDelete: schema.NoAction,
OnDelete: schema.Cascade,
},
{
Symbol: "cp_tasks_sys_user_cp_task",
Columns: []*schema.Column{CpTasksColumns[15]},
Columns: []*schema.Column{CpTasksColumns[16]},
RefColumns: []*schema.Column{SysUserColumns[0]},
OnDelete: schema.Cascade,
},
@@ -361,6 +395,7 @@ var (
}
// Tables holds all the tables in the schema.
Tables = []*schema.Table{
CourseEntityTable,
CpImageTable,
CpSchoolsTable,
CpTasksTable,
@@ -378,6 +413,10 @@ var (
)
func init() {
CourseEntityTable.ForeignKeys[0].RefTable = SysUserTable
CourseEntityTable.Annotation = &entsql.Annotation{
Table: "course_entity",
}
CpImageTable.ForeignKeys[0].RefTable = SysUserTable
CpImageTable.Annotation = &entsql.Annotation{
Table: "cp_image",

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