Compare commits
507 Commits
d9d3118879
...
ai-agent-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1be2bebf7 | ||
|
|
80dcd76749 | ||
|
|
953fbc043b | ||
|
|
64bc65114a | ||
|
|
ae9d778ac7 | ||
|
|
77a9a64a41 | ||
|
|
0c31b97824 | ||
|
|
70ae2fab44 | ||
|
|
411a9058ca | ||
|
|
4b9f845fae | ||
|
|
bdaa53bac8 | ||
|
|
e5b81c08f3 | ||
|
|
1ffab89e97 | ||
|
|
5440b226c4 | ||
|
|
90c6022839 | ||
|
|
184467e482 | ||
|
|
68045d6458 | ||
|
|
d52f17a17b | ||
|
|
047937af4c | ||
|
|
a9267bfc0e | ||
|
|
1019fd685b | ||
|
|
34246d8a62 | ||
|
|
599b6335d5 | ||
|
|
46bc48d1c1 | ||
|
|
17675e702d | ||
|
|
639c683144 | ||
|
|
96a21210b5 | ||
|
|
7495dc86a0 | ||
|
|
ee4cb20eef | ||
|
|
9ca3cd0b1a | ||
|
|
eb6ec06157 | ||
|
|
62940ae25a | ||
|
|
dfc143379f | ||
|
|
bd3a9a5ce8 | ||
|
|
ec4fdc39fe | ||
|
|
3c3e134d2b | ||
|
|
81089cc058 | ||
|
|
681194a517 | ||
|
|
8f515f76c0 | ||
|
|
fcb74eb28c | ||
|
|
44afdaef8e | ||
|
|
41c55d088d | ||
|
|
3b71fe3135 | ||
|
|
4326c41258 | ||
|
|
7f0d57b311 | ||
|
|
75c208dafc | ||
|
|
8021ca9eff | ||
|
|
2cf06a5677 | ||
|
|
2fa42cd8a3 | ||
|
|
a600eb9e7e | ||
|
|
fcf0fd7f70 | ||
|
|
4e421c160c | ||
|
|
340e2016d6 | ||
|
|
5dfaead60e | ||
|
|
c8acb12e4a | ||
|
|
5fbcb8cbd9 | ||
|
|
fd8d4399d3 | ||
|
|
6f1efafd86 | ||
|
|
2714a507d9 | ||
|
|
9a9230786b | ||
|
|
4a8b58a65c | ||
|
|
7d81f88658 | ||
|
|
0ce3c0bbdd | ||
|
|
981235e6e9 | ||
|
|
d0ecb232a1 | ||
|
|
c7a52604e7 | ||
|
|
da81b2d8a3 | ||
|
|
7b14fdd8de | ||
|
|
1fc2734eb7 | ||
|
|
f3bef72ebb | ||
|
|
7e6d2e829b | ||
|
|
944626960b | ||
|
|
c073868989 | ||
|
|
d2981100fa | ||
|
|
ce4f7e5711 | ||
|
|
cc812ba2cb | ||
|
|
5ed79c6dd0 | ||
|
|
6e2ca8f1c3 | ||
|
|
a46a552097 | ||
|
|
53e56134d4 | ||
|
|
f90105ebb4 | ||
|
|
67ed1ac1e3 | ||
|
|
69b84f6613 | ||
|
|
433d616b9b | ||
|
|
53aa575ad4 | ||
|
|
b7847c7e7d | ||
|
|
94eb41996e | ||
|
|
381b712b25 | ||
|
|
c319b0b4e4 | ||
|
|
1a32fa9e20 | ||
|
|
909406238c | ||
|
|
54a1d2a66f | ||
|
|
8dcbfcad33 | ||
|
|
f64fd43951 | ||
|
|
bfda33280a | ||
|
|
8d0411f1f4 | ||
|
|
3995d4acab | ||
|
|
6ff5727156 | ||
|
|
f654386dfe | ||
|
|
c03ef82643 | ||
|
|
525545329b | ||
|
|
755cb6f509 | ||
|
|
55469708f0 | ||
|
|
94c52c62fe | ||
|
|
37b4709d76 | ||
|
|
86555af6ce | ||
|
|
ddb00879f4 | ||
|
|
2d0ca08314 | ||
|
|
b78ecf27d5 | ||
|
|
02a5f69958 | ||
|
|
cf5bf746ef | ||
|
|
0a5e40ee25 | ||
|
|
51a266ef58 | ||
|
|
1f0901c90c | ||
|
|
a725c06396 | ||
|
|
54547f0d7c | ||
|
|
afe9c8bcae | ||
|
|
688d93e5c1 | ||
|
|
4c65b2398d | ||
|
|
41435f1aa3 | ||
|
|
20206bbc44 | ||
|
|
f2dc0d1825 | ||
|
|
51b4d1b072 | ||
|
|
9180799e4e | ||
|
|
9788b9182b | ||
|
|
260b9a4795 | ||
|
|
9380e3daa8 | ||
|
|
bb894e14a4 | ||
|
|
b492d82442 | ||
|
|
d7bcad9da7 | ||
|
|
04e11d15e2 | ||
|
|
97e3dc5eed | ||
|
|
695bd56a27 | ||
|
|
7919383be3 | ||
|
|
d6cc3c5d96 | ||
|
|
19f0d05a69 | ||
|
|
3c5e575e9b | ||
|
|
f875617de1 | ||
|
|
9976e8a6e2 | ||
|
|
38e8cbc5ca | ||
|
|
d95c14c903 | ||
|
|
ffb2f2fb4c | ||
|
|
4bd2fc357d | ||
|
|
da23d17af8 | ||
|
|
c1a6046107 | ||
|
|
d21f61646a | ||
|
|
eecdf442fb | ||
|
|
8e8338743d | ||
|
|
1b4c3cbb8d | ||
|
|
b7756e2112 | ||
|
|
cb49059e84 | ||
|
|
690cabfd96 | ||
|
|
771ecd9d81 | ||
|
|
a9b2979a21 | ||
|
|
17337b8d78 | ||
|
|
09fb43ee14 | ||
|
|
477c0e3f2c | ||
|
|
2e4f520dac | ||
|
|
067b25b9af | ||
|
|
36370c215d | ||
|
|
e24731acfe | ||
|
|
927e9df7de | ||
|
|
114b41144e | ||
|
|
5019a36138 | ||
|
|
e15eb6149b | ||
|
|
9d401a9c93 | ||
|
|
eacf86e118 | ||
|
|
c4b631c815 | ||
|
|
fb25e75a3a | ||
|
|
e9099bbe04 | ||
|
|
f02fb91175 | ||
|
|
5beef22269 | ||
|
|
933cbb91d8 | ||
|
|
efd917d184 | ||
|
|
e906208f4a | ||
|
|
cf137f6307 | ||
|
|
e6b991fe86 | ||
|
|
3e75792e43 | ||
|
|
dd3f6325bb | ||
|
|
108ba348f6 | ||
|
|
bcdcec40e0 | ||
|
|
2ce8baea42 | ||
|
|
c6425ca206 | ||
|
|
acb359ec33 | ||
|
|
a1395d9a33 | ||
|
|
609de29e71 | ||
|
|
2efed4f4a5 | ||
|
|
aec90ec9d6 | ||
|
|
1aaff2942d | ||
|
|
cdbfc5383d | ||
|
|
f302555e0c | ||
|
|
86c5890476 | ||
|
|
a13ee395c7 | ||
|
|
9abcd72aca | ||
|
|
4ddea6d468 | ||
|
|
867a2dc861 | ||
|
|
4a72e3fa0d | ||
|
|
8b4371aabb | ||
|
|
799dd08ec0 | ||
|
|
c5c22224cf | ||
|
|
2dae47e85c | ||
|
|
375dd4f797 | ||
|
|
acb2db8397 | ||
|
|
b7a3e76d0b | ||
|
|
48150b712a | ||
|
|
6db9dfc308 | ||
|
|
2d6c1f3c46 | ||
|
|
161e10d2d1 | ||
|
|
a9a2a91183 | ||
|
|
1c9a6f108e | ||
|
|
d6adf9b736 | ||
|
|
959eb3f782 | ||
|
|
7a53e0c90c | ||
|
|
533b87fc5b | ||
|
|
15713cf7fe | ||
|
|
31dc756868 | ||
|
|
52f6b6130f | ||
|
|
16945b3d5b | ||
|
|
bdc664fc44 | ||
|
|
9555ef10e0 | ||
|
|
49e6cb26fc | ||
|
|
3ace29e692 | ||
|
|
aa9dd0129b | ||
|
|
1464271fbd | ||
|
|
754f145559 | ||
|
|
6afd0cb955 | ||
|
|
d32906702a | ||
|
|
9bcdaf6bd8 | ||
|
|
db82a8cf08 | ||
|
|
a9e8b2b01f | ||
|
|
85bd1ce8d6 | ||
|
|
4d09243efd | ||
|
|
5934056fe6 | ||
|
|
2a81062fa3 | ||
|
|
fdc868323f | ||
|
|
593b3a4cdd | ||
|
|
2b12e18e6c | ||
|
|
345ed80ec8 | ||
|
|
29dc1ae250 | ||
|
|
9fdd41b134 | ||
|
|
31ee5e8ffb | ||
|
|
d7922bb71d | ||
|
|
fa3ac91ba4 | ||
|
|
e7c152e955 | ||
|
|
0223b5c104 | ||
|
|
9e41a7c446 | ||
|
|
2ba0ffc1b7 | ||
|
|
7d038e1266 | ||
|
|
b98285f314 | ||
|
|
73438da666 | ||
|
|
85f2e1b579 | ||
|
|
ece89ebad0 | ||
|
|
6e2dd39246 | ||
|
|
a61286e534 | ||
|
|
4f944a5466 | ||
|
|
d29aac088a | ||
|
|
8abd122773 | ||
|
|
08084aa0bc | ||
|
|
e69cd5a73c | ||
|
|
76aa3bdc64 | ||
|
|
93251104af | ||
|
|
3cae477f3e | ||
|
|
25c736dc0a | ||
|
|
96a09d8980 | ||
|
|
72387235a0 | ||
|
|
1b00e505b7 | ||
|
|
1c54e47b9e | ||
|
|
ba07e2c905 | ||
|
|
bde4611a50 | ||
|
|
e7326fea7b | ||
|
|
d13b23ad2e | ||
|
|
8b1830a711 | ||
|
|
b70c530754 | ||
|
|
d90e24f9ed | ||
|
|
1fbd521d1a | ||
|
|
2ae6183e7f | ||
|
|
7905911624 | ||
|
|
c5b6b33d8e | ||
|
|
5d29fd6d3b | ||
|
|
ad8f48f36b | ||
|
|
f9843c13d4 | ||
|
|
6bd561b094 | ||
|
|
d2c6238df1 | ||
|
|
1d108983e8 | ||
|
|
b768bca638 | ||
|
|
28fcd6c9ce | ||
|
|
10559a925c | ||
|
|
942e218a9e | ||
|
|
f6af9edc38 | ||
|
|
e0f6331ec3 | ||
|
|
06f0c6caa7 | ||
|
|
56ec260e3a | ||
|
|
176cf84369 | ||
|
|
6de3b722ed | ||
|
|
2cf6326764 | ||
|
|
ec27ee58b4 | ||
|
|
4e42e2202e | ||
|
|
e60d8eceb7 | ||
|
|
08fb939b38 | ||
|
|
482dd73afd | ||
|
|
f09a9fee75 | ||
|
|
9a31d14b41 | ||
|
|
2fd7f88f04 | ||
|
|
9d4cc802e9 | ||
|
|
ee6b4827fa | ||
|
|
48d8c528f6 | ||
|
|
40c0a5ac64 | ||
|
|
3a60bcc174 | ||
|
|
2b3fad16fd | ||
|
|
f0cf6bf5c8 | ||
|
|
0ba4e3240b | ||
|
|
9332b17fc1 | ||
|
|
4ec4023f40 | ||
|
|
d9971541f2 | ||
|
|
7b0e4fcc73 | ||
|
|
cfde73d13a | ||
|
|
c17c9000a8 | ||
|
|
42d537a68b | ||
|
|
25eebec8f7 | ||
|
|
bbe5b01872 | ||
|
|
6b31536de5 | ||
|
|
2e5db5500f | ||
|
|
7038d31c53 | ||
|
|
3eb27c3d35 | ||
|
|
a9c3a1bcec | ||
|
|
384926e73a | ||
|
|
4335c12659 | ||
|
|
e6e4829164 | ||
|
|
f3c67cf598 | ||
|
|
4681d468ce | ||
|
|
63e7d3d5f5 | ||
|
|
f47d8c8ce3 | ||
|
|
6f69f45ddc | ||
|
|
e73678c788 | ||
|
|
09a2f91cbf | ||
|
|
29da7499a4 | ||
|
|
5b024e9443 | ||
|
|
225932eff1 | ||
|
|
65d5f5ae86 | ||
|
|
3e647ef14d | ||
|
|
7cb3aea2e6 | ||
|
|
7f4b8f1c8a | ||
|
|
0a2710b865 | ||
|
|
2a301c4983 | ||
|
|
faa8131a1b | ||
|
|
71bd885bd0 | ||
|
|
691a1e50f0 | ||
|
|
ef6e9fd16d | ||
|
|
17f9ac6d54 | ||
|
|
3f8e6e48c0 | ||
|
|
bda4fdf69d | ||
|
|
5c85ed13fd | ||
|
|
1986901031 | ||
|
|
e1d3ec21e5 | ||
|
|
f45283dade | ||
|
|
31c44d8df7 | ||
|
|
bf443963c8 | ||
|
|
a0eb234539 | ||
|
|
b6d670c240 | ||
|
|
b5fb2c42c6 | ||
|
|
d72cc529ba | ||
|
|
660bd00cae | ||
|
|
b5489711ec | ||
|
|
76717c4f8a | ||
|
|
3d53d0bcd6 | ||
|
|
c7c9428b68 | ||
|
|
991a970d6a | ||
|
|
cbe93b9f7e | ||
|
|
5d7217b775 | ||
|
|
d6836b8bcf | ||
|
|
c367651c78 | ||
|
|
77123fd971 | ||
|
|
9d73a6837b | ||
|
|
651f0157dc | ||
|
|
90e8dbe449 | ||
|
|
ccba2667bc | ||
|
|
3ce9fc9790 | ||
|
|
2f24dd77bf | ||
|
|
2bc07cb3df | ||
|
|
30678dbbb4 | ||
|
|
c5b0f69b51 | ||
|
|
e593f2cba4 | ||
|
|
10f7499066 | ||
|
|
36b7e495f7 | ||
|
|
94b96e3c19 | ||
|
|
0d1ee18da0 | ||
|
|
cab0b61ee0 | ||
|
|
8e6611d76d | ||
|
|
1ef82e5f93 | ||
|
|
43dc962606 | ||
|
|
020d674ca2 | ||
|
|
bb0e1081cc | ||
|
|
5162f9ce3b | ||
|
|
57fae7fe4b | ||
|
|
17412d7de7 | ||
|
|
d59f40dfba | ||
|
|
5953be63cb | ||
|
|
58a4311947 | ||
|
|
c5a9b9a15f | ||
|
|
716c344780 | ||
|
|
9af8c4897b | ||
|
|
ca72024a68 | ||
|
|
0d2bc585a9 | ||
|
|
4e3edefb35 | ||
|
|
9408242726 | ||
|
|
4710208e81 | ||
|
|
4fc6a1e818 | ||
|
|
c9b79a074b | ||
|
|
2e79eb346f | ||
|
|
3f88bd4158 | ||
|
|
f58e079741 | ||
|
|
6f1eb1f4b9 | ||
|
|
826d529997 | ||
|
|
43e60eab4a | ||
|
|
6c33024790 | ||
|
|
d27e625fde | ||
|
|
23cecb9360 | ||
|
|
7e4c835ced | ||
|
|
aff460f555 | ||
|
|
52961b459e | ||
|
|
0af2f867fc | ||
|
|
85e291e0b8 | ||
|
|
6d8a859b20 | ||
|
|
a70dfb0769 | ||
|
|
c637d412e6 | ||
|
|
e996bc2d7f | ||
|
|
15be047371 | ||
|
|
0a0e0bca10 | ||
|
|
9a8f3bd161 | ||
|
|
7e2c035692 | ||
|
|
44b2ade9bc | ||
|
|
1200d02fbf | ||
|
|
b020f48325 | ||
|
|
917857f1ff | ||
|
|
69d8ff1034 | ||
|
|
9a334101ca | ||
|
|
ee53b3d9c4 | ||
|
|
01a5ad5302 | ||
|
|
f12f0e1f84 | ||
|
|
6aefcdbed8 | ||
|
|
3d22a2ef65 | ||
|
|
a33c6dbf1a | ||
|
|
2b7c779e14 | ||
|
|
0e36f7c0b3 | ||
|
|
228a309545 | ||
|
|
b15ad8eb5e | ||
|
|
a525735b0b | ||
|
|
6a58af8dfb | ||
|
|
0089e63832 | ||
|
|
d4f00eb89f | ||
|
|
d15e6e395b | ||
|
|
39eb4bef07 | ||
|
|
03de576d8c | ||
|
|
216b57a4c7 | ||
|
|
5383d2d40e | ||
|
|
1d7a2013e3 | ||
|
|
24d2908cca | ||
|
|
330845a387 | ||
|
|
bbedd01a72 | ||
|
|
2b07061c18 | ||
|
|
01a3c81359 | ||
|
|
96e275efa6 | ||
|
|
12eb6c73c3 | ||
|
|
4166eddd28 | ||
|
|
6ea1592c19 | ||
|
|
f8799a073c | ||
|
|
0eb83fc930 | ||
|
|
a5dd3946f8 | ||
|
|
2732df24af | ||
|
|
f0ae27a50b | ||
|
|
c5037ea397 | ||
|
|
695aaedfba | ||
|
|
4f71d874bd | ||
|
|
c69729fadd | ||
|
|
64d04996af | ||
|
|
8eea510583 | ||
|
|
04c2b246f6 | ||
|
|
a46eb176d7 | ||
|
|
2bea88f1a3 | ||
|
|
06617de984 | ||
|
|
6459d7c024 | ||
|
|
bd4af8039f | ||
|
|
8aaa22cea3 | ||
|
|
7d902682f8 | ||
|
|
a81be99100 | ||
|
|
b6dfe93d2c | ||
|
|
35aa022984 | ||
|
|
dfe2d4cc37 | ||
|
|
1d16502d32 | ||
|
|
25c88187a3 | ||
|
|
ac04e846fa | ||
|
|
29985e2118 | ||
|
|
3b74dfd49a | ||
|
|
6abcc49ed4 | ||
|
|
f16e1cd7a6 | ||
|
|
4341b8a24b | ||
|
|
a89e11d132 | ||
|
|
bc91a8cff2 | ||
|
|
8040010b98 | ||
|
|
b39f15c798 | ||
|
|
c3cf49c63e | ||
|
|
899bd7e316 | ||
|
|
890727d495 | ||
|
|
8a8e69596a | ||
|
|
58fcc92e4d | ||
|
|
0cd795f57a | ||
|
|
4830be6388 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -265,6 +265,7 @@ src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
|
||||
**/wwwroot/libs/*
|
||||
public
|
||||
dist
|
||||
dist - 副本
|
||||
.vscode
|
||||
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
|
||||
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Production.json
|
||||
@@ -277,3 +278,5 @@ database_backup
|
||||
/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db
|
||||
|
||||
package-lock.json
|
||||
|
||||
.claude
|
||||
|
||||
11
Yi.Abp.Net8/.claude/settings.local.json
Normal file
11
Yi.Abp.Net8/.claude/settings.local.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(dotnet build \"E:\\code\\github\\Yi\\Yi.Abp.Net8\\module\\ai-hub\\Yi.Framework.AiHub.Application\\Yi.Framework.AiHub.Application.csproj\" --no-restore)",
|
||||
"Read(//e/code/github/Yi/Yi.Ai.Vue3/**)",
|
||||
"Bash(dotnet build:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
129
Yi.Abp.Net8/CLAUDE.md
Normal file
129
Yi.Abp.Net8/CLAUDE.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
Yi.Abp.Net8 is a modular, multi-tenant SaaS platform built on ABP Framework 8.3.4 with .NET 8.0. It uses **SqlSugar** (not EF Core) as the ORM and follows Domain-Driven Design (DDD) principles. The platform includes AI/ML features (chat, models, agents, token tracking), RBAC, forum, messaging, and digital collectibles modules.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Solution Structure
|
||||
|
||||
```
|
||||
Yi.Abp.Net8/
|
||||
├── src/ # Main application host
|
||||
│ └── Yi.Abp.Web/ # ASP.NET Core 8.0 Web Host (port 19001)
|
||||
├── framework/ # Framework layer (shared infrastructure)
|
||||
│ ├── Yi.Framework.Core # Core utilities, JSON handling
|
||||
│ ├── Yi.Framework.SqlSugarCore # SqlSugar ORM abstraction (v5.1.4.197-preview22)
|
||||
│ ├── Yi.Framework.SqlSugarCore.Abstractions # Repository interfaces
|
||||
│ ├── Yi.Framework.AspNetCore # ASP.NET Core extensions
|
||||
│ ├── Yi.Framework.AspNetCore.Authentication.OAuth # QQ, Gitee OAuth
|
||||
│ ├── Yi.Framework.Ddd.Application # DDD application base classes
|
||||
│ ├── Yi.Framework.BackgroundWorkers.Hangfire # Job scheduling
|
||||
│ ├── Yi.Framework.Caching.FreeRedis # Redis caching
|
||||
│ └── Yi.Framework.SemanticKernel # AI/ML integration
|
||||
├── module/ # Business modules (each follows 5-layer DDD pattern)
|
||||
│ ├── ai-hub/ # AI services (chat, models, sessions, agents)
|
||||
│ ├── rbac/ # Role-Based Access Control (core auth/authz)
|
||||
│ ├── bbs/ # Forum/community
|
||||
│ ├── chat-hub/ # Real-time messaging
|
||||
│ ├── audit-logging/ # Audit trail tracking
|
||||
│ ├── code-gen/ # Code generation
|
||||
│ ├── tenant-management/ # Multi-tenancy support
|
||||
│ ├── digital-collectibles/ # NFT/digital assets
|
||||
│ └── ai-stock/ # AI-powered stock analysis
|
||||
├── test/ # Unit tests (xUnit + NSubstitute + Shouldly)
|
||||
├── client/ # HTTP API clients
|
||||
└── tool/ # Development utilities
|
||||
```
|
||||
|
||||
### Module Structure (DDD Layers)
|
||||
|
||||
Each module follows this pattern:
|
||||
|
||||
```
|
||||
module/[feature]/
|
||||
├── [Feature].Domain.Shared/ # Constants, enums, shared DTOs
|
||||
├── [Feature].Domain/ # Entities, aggregates, domain services
|
||||
├── [Feature].Application.Contracts/ # Service interfaces, DTOs
|
||||
├── [Feature].Application/ # Application services (implementations)
|
||||
└── [Feature].SqlSugarCore/ # Repository implementations, DbContext
|
||||
```
|
||||
|
||||
**Dependency Flow:** Application → Domain + Application.Contracts → Domain.Shared → Framework
|
||||
|
||||
### Module Registration
|
||||
|
||||
All modules are registered in `src/Yi.Abp.Web/YiAbpWebModule.cs`. When adding a new module:
|
||||
|
||||
1. Add `DependsOn` attribute in `YiAbpWebModule`
|
||||
2. Add conventional controller in `PreConfigureServices`:
|
||||
```csharp
|
||||
options.ConventionalControllers.Create(typeof(YiFramework[Feature]ApplicationModule).Assembly,
|
||||
option => option.RemoteServiceName = "[service-name]");
|
||||
```
|
||||
|
||||
### API Routing
|
||||
|
||||
All API routes use the unified prefix: `/api/app/{service-name}/{controller}/{action}`
|
||||
|
||||
Registered service names:
|
||||
- `default` - Main application services
|
||||
- `rbac` - Authentication, authorization, user/role management
|
||||
- `ai-hub` - AI chat, models, sessions, agents
|
||||
- `bbs` - Forum posts and comments
|
||||
- `chat-hub` - Real-time messaging
|
||||
- `tenant-management` - Multi-tenant configuration
|
||||
- `code-gen` - Code generation utilities
|
||||
- `digital-collectibles` - NFT/digital assets
|
||||
- `ai-stock` - Stock analysis
|
||||
|
||||
## Database
|
||||
|
||||
**ORM:** SqlSugar (NOT Entity Framework Core)
|
||||
|
||||
**Configuration** (`appsettings.json`):
|
||||
```json
|
||||
"DbConnOptions": {
|
||||
"Url": "DataSource=yi-abp-dev.db",
|
||||
"DbType": "Sqlite", // Sqlite, Mysql, Sqlserver, Oracle, PostgreSQL
|
||||
"EnabledCodeFirst": true, // Auto-create tables
|
||||
"EnabledDbSeed": true, // Auto-seed data
|
||||
"EnabledSaasMultiTenancy": true
|
||||
}
|
||||
```
|
||||
|
||||
**Multi-tenancy:** Tenant isolation via `HeaderTenantResolveContributor` (NOT cookie-based). Tenant is resolved from `__tenant` header.
|
||||
|
||||
## Key Technologies
|
||||
|
||||
| Component | Technology |
|
||||
|-----------|-----------|
|
||||
| Framework | ABP 8.3.4 |
|
||||
| .NET Version | .NET 8.0 |
|
||||
| ORM | SqlSugar 5.1.4.197-preview22 |
|
||||
| Authentication | JWT Bearer + Refresh Tokens |
|
||||
| OAuth | QQ, Gitee |
|
||||
| Caching | FreeRedis (optional) |
|
||||
| Background Jobs | Hangfire (in-memory or Redis) |
|
||||
| Logging | Serilog (daily rolling files) |
|
||||
| Testing | xUnit + NSubstitute + Shouldly |
|
||||
| Container | Autofac |
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **JSON Serialization:** Uses Microsoft's `System.Text.Json`, NOT Newtonsoft.Json. Date format handled via `DatetimeJsonConverter`.
|
||||
- **Multi-tenancy:** Tenant resolved from `__tenant` header, NOT cookies.
|
||||
- **Rate Limiting:** Disabled in development, enabled in production (1000 req/60s sliding window).
|
||||
- **Swagger:** Available at `/swagger` in development.
|
||||
- **Hangfire Dashboard:** Available at `/hangfire` (requires JWT authorization).
|
||||
- **Background Workers:** Disabled in development (`AbpBackgroundWorkerOptions.IsEnabled = false`).
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Branch:** Main branch is `abp`, current development branch is `ai-hub`.
|
||||
2. **Commit Convention:** Chinese descriptive messages with prefixes (`feat:`, `fix:`, `style:`).
|
||||
3. **Testing:** Run `dotnet test` before committing.
|
||||
4. **Building:** Use `dotnet build --no-restore` for faster builds after initial restore.
|
||||
@@ -186,6 +186,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Stock.Domain.S
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Stock.SqlSugarCore", "module\ai-stock\Yi.Framework.Stock.SqlSugarCore\Yi.Framework.Stock.SqlSugarCore.csproj", "{5F49318F-E6C7-4194-BAE0-83D4FB8D1983}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ai-hub", "ai-hub", "{7AD5DBAE-44F9-474B-8F7B-837EDE908934}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.AiHub.Application", "module\ai-hub\Yi.Framework.AiHub.Application\Yi.Framework.AiHub.Application.csproj", "{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.AiHub.Application.Contracts", "module\ai-hub\Yi.Framework.AiHub.Application.Contracts\Yi.Framework.AiHub.Application.Contracts.csproj", "{123D1C81-D667-4060-8E85-FFE7FB4584AD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.AiHub.Domain", "module\ai-hub\Yi.Framework.AiHub.Domain\Yi.Framework.AiHub.Domain.csproj", "{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.AiHub.Domain.Shared", "module\ai-hub\Yi.Framework.AiHub.Domain.Shared\Yi.Framework.AiHub.Domain.Shared.csproj", "{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.AiHub.SqlSugarCore", "module\ai-hub\Yi.Framework.AiHub.SqlSugarCore\Yi.Framework.AiHub.SqlSugarCore.csproj", "{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -468,6 +480,26 @@ Global
|
||||
{5F49318F-E6C7-4194-BAE0-83D4FB8D1983}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5F49318F-E6C7-4194-BAE0-83D4FB8D1983}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5F49318F-E6C7-4194-BAE0-83D4FB8D1983}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{123D1C81-D667-4060-8E85-FFE7FB4584AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{123D1C81-D667-4060-8E85-FFE7FB4584AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{123D1C81-D667-4060-8E85-FFE7FB4584AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{123D1C81-D667-4060-8E85-FFE7FB4584AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -551,6 +583,12 @@ Global
|
||||
{162821E4-8FE0-4A68-B3C0-49BD6596446F} = {DB46873F-981A-43D8-91B0-D464CCB65943}
|
||||
{10273544-715D-4BB3-893C-6F010D947BDD} = {DB46873F-981A-43D8-91B0-D464CCB65943}
|
||||
{5F49318F-E6C7-4194-BAE0-83D4FB8D1983} = {DB46873F-981A-43D8-91B0-D464CCB65943}
|
||||
{7AD5DBAE-44F9-474B-8F7B-837EDE908934} = {2317227D-7796-4E7B-BEDB-7CD1CAE7B853}
|
||||
{1AD10DD2-535E-4EAB-A8A4-EC3FCA206895} = {7AD5DBAE-44F9-474B-8F7B-837EDE908934}
|
||||
{123D1C81-D667-4060-8E85-FFE7FB4584AD} = {7AD5DBAE-44F9-474B-8F7B-837EDE908934}
|
||||
{8EB4C8BB-6B21-4811-9FAB-B98FA5CA754D} = {7AD5DBAE-44F9-474B-8F7B-837EDE908934}
|
||||
{5FC6CA90-D5B4-433E-9B2C-94330FFB4C48} = {7AD5DBAE-44F9-474B-8F7B-837EDE908934}
|
||||
{8698C812-4DDC-4E80-BCD6-24C5D56AEDB1} = {7AD5DBAE-44F9-474B-8F7B-837EDE908934}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {23D6FBC9-C970-4641-BC1E-2AEA59F51C18}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Yi.Framework.Core.Authentication;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Microsoft.AspNetCore.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// 可刷新的鉴权提供者
|
||||
/// </summary>
|
||||
public class RefreshAuthenticationHandlerProvider : IRefreshAuthenticationHandlerProvider
|
||||
{
|
||||
private Dictionary<string, IAuthenticationHandler> _handlerMap =
|
||||
new Dictionary<string, IAuthenticationHandler>((IEqualityComparer<string>)StringComparer.Ordinal);
|
||||
|
||||
/// <summary>Constructor.</summary>
|
||||
/// <param name="schemes">The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider" />.</param>
|
||||
public RefreshAuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes)
|
||||
{
|
||||
this.Schemes = schemes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider" />.
|
||||
/// </summary>
|
||||
public IAuthenticationSchemeProvider Schemes { get; }
|
||||
|
||||
/// <summary>Returns the handler instance that will be used.</summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
|
||||
/// <returns>The handler instance.</returns>
|
||||
public async Task<IAuthenticationHandler?> GetHandlerAsync(
|
||||
HttpContext context,
|
||||
string authenticationScheme)
|
||||
{
|
||||
IAuthenticationHandler handlerAsync;
|
||||
if (this._handlerMap.TryGetValue(authenticationScheme, out handlerAsync))
|
||||
return handlerAsync;
|
||||
AuthenticationScheme schemeAsync = await this.Schemes.GetSchemeAsync(authenticationScheme);
|
||||
if (schemeAsync == null)
|
||||
return (IAuthenticationHandler)null;
|
||||
|
||||
if ((context.RequestServices.GetService(schemeAsync.HandlerType) ??
|
||||
ActivatorUtilities.CreateInstance(context.RequestServices, schemeAsync.HandlerType)) is
|
||||
IAuthenticationHandler handler)
|
||||
{
|
||||
handlerAsync = handler;
|
||||
await handler.InitializeAsync(schemeAsync, context);
|
||||
this._handlerMap[authenticationScheme] = handler;
|
||||
}
|
||||
|
||||
return handlerAsync;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 刷新鉴权
|
||||
/// </summary>
|
||||
public void RefreshAuthentication()
|
||||
{
|
||||
_handlerMap = new Dictionary<string, IAuthenticationHandler>((IEqualityComparer<string>)StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,12 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Volo.Abp.AspNetCore.Mvc;
|
||||
using Volo.Abp.AspNetCore.Mvc.Conventions;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Options;
|
||||
|
||||
namespace Yi.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.AspNetCore.Mvc;
|
||||
using Volo.Abp.AspNetCore.WebClientInfo;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Modularity;
|
||||
using Yi.Framework.AspNetCore.Mvc;
|
||||
using Yi.Framework.AspNetCore.Microsoft.AspNetCore.Authentication;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.Core.Authentication;
|
||||
|
||||
namespace Yi.Framework.AspNetCore
|
||||
{
|
||||
@@ -35,8 +24,14 @@ namespace Yi.Framework.AspNetCore
|
||||
// 替换默认的WebClientInfoProvider为支持代理的实现
|
||||
services.Replace(new ServiceDescriptor(
|
||||
typeof(IWebClientInfoProvider),
|
||||
typeof(RealIpHttpContextWebClientInfoProvider),
|
||||
typeof(RealIpHttpContextWebClientInfoProvider),
|
||||
ServiceLifetime.Transient));
|
||||
|
||||
// 替换默认的AuthenticationHandlerProvider为支持刷新鉴权
|
||||
services.Replace(new ServiceDescriptor(
|
||||
typeof(IAuthenticationHandlerProvider),
|
||||
typeof(RefreshAuthenticationHandlerProvider),
|
||||
ServiceLifetime.Scoped));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Volo.Abp.BackgroundJobs.Hangfire;
|
||||
using Volo.Abp.BackgroundWorkers;
|
||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||
@@ -32,13 +33,19 @@ public sealed class YiFrameworkBackgroundWorkersHangfireModule : AbpModule
|
||||
/// <param name="context">应用程序初始化上下文</param>
|
||||
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
|
||||
{
|
||||
if (!context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundWorkerOptions>>().Value.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 获取后台任务管理器和所有 Hangfire 后台任务
|
||||
var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
|
||||
var workers = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
|
||||
|
||||
// 获取配置
|
||||
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||
|
||||
|
||||
// 检查是否启用 Redis
|
||||
var isRedisEnabled = configuration.GetValue<bool>("Redis:IsEnabled");
|
||||
|
||||
@@ -56,11 +63,11 @@ public sealed class YiFrameworkBackgroundWorkersHangfireModule : AbpModule
|
||||
{
|
||||
// 内存模式:直接使用 Hangfire
|
||||
var unProxyWorker = ProxyHelper.UnProxy(worker);
|
||||
|
||||
|
||||
// 添加或更新循环任务
|
||||
RecurringJob.AddOrUpdate(
|
||||
worker.RecurringJobId,
|
||||
(Expression<Func<Task>>)(() =>
|
||||
(Expression<Func<Task>>)(() =>
|
||||
((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(default)),
|
||||
worker.CronExpression,
|
||||
new RecurringJobOptions
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Yi.Framework.Core.Authentication;
|
||||
|
||||
public static class AuthenticationExtensions
|
||||
{
|
||||
public static void RefreshAuthentication(this HttpContext context)
|
||||
{
|
||||
var currentAuthenticationHandler =
|
||||
context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
|
||||
if (currentAuthenticationHandler is IRefreshAuthenticationHandlerProvider refreshAuthenticationHandler)
|
||||
{
|
||||
refreshAuthenticationHandler.RefreshAuthentication();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace Yi.Framework.Core.Authentication;
|
||||
|
||||
public interface IRefreshAuthenticationHandlerProvider: IAuthenticationHandlerProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷新鉴权
|
||||
/// </summary>
|
||||
void RefreshAuthentication();
|
||||
}
|
||||
@@ -199,11 +199,11 @@ namespace Yi.Framework.Ddd.Application
|
||||
/// <summary>
|
||||
/// 批量删除实体
|
||||
/// </summary>
|
||||
/// <param name="ids">实体ID集合</param>
|
||||
/// <param name="id">实体ID集合</param>
|
||||
[RemoteService(isEnabled: true)]
|
||||
public virtual async Task DeleteAsync(IEnumerable<TKey> ids)
|
||||
public virtual async Task DeleteAsync(IEnumerable<TKey> id)
|
||||
{
|
||||
await Repository.DeleteManyAsync(ids);
|
||||
await Repository.DeleteManyAsync(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\common.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SemanticKernel" Version="1.57.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Yi.Framework.Core.Options;
|
||||
|
||||
namespace Yi.Framework.SemanticKernel;
|
||||
|
||||
public class YiFrameworkSemanticKernelModule : AbpModule
|
||||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
var configuration = context.Services.GetConfiguration();
|
||||
var services = context.Services;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Nito.AsyncEx;
|
||||
using SqlSugar;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
|
||||
/// <summary>
|
||||
/// 批量生成激活码输入
|
||||
/// </summary>
|
||||
public class ActivationCodeCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public ActivationCodeGoodsTypeEnum GoodsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public int Count { get; set; } = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量生成激活码列表输入
|
||||
/// </summary>
|
||||
public class ActivationCodeCreateListInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成项列表
|
||||
/// </summary>
|
||||
public List<ActivationCodeCreateInput> Items { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
|
||||
/// <summary>
|
||||
/// 批量生成激活码输出
|
||||
/// </summary>
|
||||
public class ActivationCodeCreateOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public ActivationCodeGoodsTypeEnum GoodsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数量
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 激活码列表
|
||||
/// </summary>
|
||||
public List<string> Codes { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量生成激活码列表输出
|
||||
/// </summary>
|
||||
public class ActivationCodeCreateListOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 分组输出
|
||||
/// </summary>
|
||||
public List<ActivationCodeCreateOutput> Items { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
|
||||
/// <summary>
|
||||
/// 激活码兑换输入
|
||||
/// </summary>
|
||||
public class ActivationCodeRedeemInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 激活码
|
||||
/// </summary>
|
||||
public string Code { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
|
||||
/// <summary>
|
||||
/// 激活码兑换输出
|
||||
/// </summary>
|
||||
public class ActivationCodeRedeemOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public ActivationCodeGoodsTypeEnum GoodsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品名称
|
||||
/// </summary>
|
||||
public string PackageName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 内容描述
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Yi.Framework.Rbac.Domain.Shared.Dtos;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class AiUserRoleMenuDto:UserRoleMenuDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否绑定服务号
|
||||
/// </summary>
|
||||
public bool IsBindFuwuhao { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为VIP用户
|
||||
/// </summary>
|
||||
public bool IsVip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// VIP到期时间
|
||||
/// </summary>
|
||||
public DateTime? VipExpireTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Announcement;
|
||||
|
||||
/// <summary>
|
||||
/// 公告缓存 DTO
|
||||
/// </summary>
|
||||
public class AnnouncementCacheDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 版本号
|
||||
/// </summary>
|
||||
public string Version { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 公告日志列表
|
||||
/// </summary>
|
||||
public List<AnnouncementLogDto> Logs { get; set; } = new List<AnnouncementLogDto>();
|
||||
|
||||
/// <summary>
|
||||
/// 跳转链接
|
||||
/// </summary>
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Announcement;
|
||||
|
||||
/// <summary>
|
||||
/// 公告日志 DTO
|
||||
/// </summary>
|
||||
public class AnnouncementLogDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内容列表
|
||||
/// </summary>
|
||||
public List<string> Content { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 图片url
|
||||
/// </summary>
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开始时间(系统公告时间、活动开始时间)
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 活动结束时间
|
||||
/// </summary>
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公告类型(系统、活动)
|
||||
/// </summary>
|
||||
public AnnouncementTypeEnum Type{ get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 跳转链接
|
||||
/// </summary>
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Announcement;
|
||||
|
||||
/// <summary>
|
||||
/// 公告输出 DTO
|
||||
/// </summary>
|
||||
public class AnnouncementOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 版本号
|
||||
/// </summary>
|
||||
public string Version { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 公告日志列表
|
||||
/// </summary>
|
||||
public List<AnnouncementLogDto> Logs { get; set; } = new List<AnnouncementLogDto>();
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌任务状态输出
|
||||
/// </summary>
|
||||
public class CardFlipStatusOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 本周总翻牌次数
|
||||
/// </summary>
|
||||
public int TotalFlips { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余免费次数
|
||||
/// </summary>
|
||||
public int RemainingFreeFlips { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余赠送次数
|
||||
/// </summary>
|
||||
public int RemainingBonusFlips { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余邀请解锁次数
|
||||
/// </summary>
|
||||
public int RemainingInviteFlips { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以翻牌
|
||||
/// </summary>
|
||||
public bool CanFlip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户的邀请码
|
||||
/// </summary>
|
||||
public string? MyInviteCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本周邀请人数
|
||||
/// </summary>
|
||||
public int InvitedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌记录
|
||||
/// </summary>
|
||||
public List<CardFlipRecord> FlipRecords { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 下次可翻牌提示
|
||||
/// </summary>
|
||||
public string? NextFlipTip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前用户是否已经填写过邀请码
|
||||
/// </summary>
|
||||
public bool IsFilledInviteCode { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌记录
|
||||
/// </summary>
|
||||
public class CardFlipRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// 翻牌序号(1-10)
|
||||
/// </summary>
|
||||
public int FlipNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已翻
|
||||
/// </summary>
|
||||
public bool IsFlipped { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中奖
|
||||
/// </summary>
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 奖励金额(token数)
|
||||
/// </summary>
|
||||
public long? RewardAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌类型描述
|
||||
/// </summary>
|
||||
public string? FlipTypeDesc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 在翻牌顺序中的位置(1-10,表示第几个翻)
|
||||
/// </summary>
|
||||
public int FlipOrderIndex { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌输入
|
||||
/// </summary>
|
||||
public class FlipCardInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 翻牌序号(1-10)
|
||||
/// </summary>
|
||||
public int FlipNumber { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌输出
|
||||
/// </summary>
|
||||
public class FlipCardOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 翻牌序号(1-10)
|
||||
/// </summary>
|
||||
public int FlipNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否中奖
|
||||
/// </summary>
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 奖励金额(token数)
|
||||
/// </summary>
|
||||
public long? RewardAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 奖励描述
|
||||
/// </summary>
|
||||
public string? RewardDesc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余可翻次数
|
||||
/// </summary>
|
||||
public int RemainingFlips { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
/// <summary>
|
||||
/// 邀请码信息输出
|
||||
/// </summary>
|
||||
public class InviteCodeOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 我的邀请码
|
||||
/// </summary>
|
||||
public string? MyInviteCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本周邀请人数
|
||||
/// </summary>
|
||||
public int InvitedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已被邀请
|
||||
/// </summary>
|
||||
public bool IsInvited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邀请历史记录
|
||||
/// </summary>
|
||||
public List<InvitationHistoryItem> InvitationHistory { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 邀请历史记录项
|
||||
/// </summary>
|
||||
public class InvitationHistoryItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 被邀请人昵称(脱敏)
|
||||
/// </summary>
|
||||
public string InvitedUserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 邀请时间
|
||||
/// </summary>
|
||||
public DateTime InvitationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 本周所在
|
||||
/// </summary>
|
||||
public string WeekDescription { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
/// <summary>
|
||||
/// 使用邀请码输入
|
||||
/// </summary>
|
||||
public class UseInviteCodeInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 邀请码
|
||||
/// </summary>
|
||||
public string InviteCode { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI应用输入
|
||||
/// </summary>
|
||||
public class AiAppCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用名称
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用名称不能为空")]
|
||||
[StringLength(100, ErrorMessage = "应用名称不能超过100个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用终结点
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用终结点不能为空")]
|
||||
[StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")]
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外URL
|
||||
/// </summary>
|
||||
[StringLength(500, ErrorMessage = "额外URL不能超过500个字符")]
|
||||
public string? ExtraUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用Key
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用Key不能为空")]
|
||||
[StringLength(500, ErrorMessage = "应用Key不能超过500个字符")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||
public int OrderNum { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// AI应用DTO
|
||||
/// </summary>
|
||||
public class AiAppDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用终结点
|
||||
/// </summary>
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外URL
|
||||
/// </summary>
|
||||
public string? ExtraUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用Key
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI应用列表输入
|
||||
/// </summary>
|
||||
public class AiAppGetListInput : PagedAllResultRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 搜索关键词(搜索应用名称)
|
||||
/// </summary>
|
||||
public string? SearchKey { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI应用输入
|
||||
/// </summary>
|
||||
public class AiAppUpdateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用ID不能为空")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用名称
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用名称不能为空")]
|
||||
[StringLength(100, ErrorMessage = "应用名称不能超过100个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用终结点
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用终结点不能为空")]
|
||||
[StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")]
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外URL
|
||||
/// </summary>
|
||||
[StringLength(500, ErrorMessage = "额外URL不能超过500个字符")]
|
||||
public string? ExtraUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用Key
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "应用Key不能为空")]
|
||||
[StringLength(500, ErrorMessage = "应用Key不能超过500个字符")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||
public int OrderNum { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI模型输入
|
||||
/// </summary>
|
||||
public class AiModelCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理名
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "处理名不能为空")]
|
||||
[StringLength(100, ErrorMessage = "处理名不能超过100个字符")]
|
||||
public string HandlerName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型ID不能为空")]
|
||||
[StringLength(200, ErrorMessage = "模型ID不能超过200个字符")]
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型名称不能为空")]
|
||||
[StringLength(200, ErrorMessage = "模型名称不能超过200个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型描述
|
||||
/// </summary>
|
||||
[StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AI应用ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "AI应用ID不能为空")]
|
||||
public Guid AiAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外信息
|
||||
/// </summary>
|
||||
[StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")]
|
||||
public string? ExtraInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型类型不能为空")]
|
||||
public ModelTypeEnum ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型API类型
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型API类型不能为空")]
|
||||
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型倍率
|
||||
/// </summary>
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")]
|
||||
public decimal Multiplier { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 模型显示倍率
|
||||
/// </summary>
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")]
|
||||
public decimal MultiplierShow { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 供应商分组名称
|
||||
/// </summary>
|
||||
[StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")]
|
||||
public string? ProviderName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型图标URL
|
||||
/// </summary>
|
||||
[StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为尊享模型
|
||||
/// </summary>
|
||||
public bool IsPremium { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// AI模型DTO
|
||||
/// </summary>
|
||||
public class AiModelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 处理名
|
||||
/// </summary>
|
||||
public string HandlerName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型描述
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AI应用ID
|
||||
/// </summary>
|
||||
public Guid AiAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外信息
|
||||
/// </summary>
|
||||
public string? ExtraInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
public ModelTypeEnum ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型API类型
|
||||
/// </summary>
|
||||
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型倍率
|
||||
/// </summary>
|
||||
public decimal Multiplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型显示倍率
|
||||
/// </summary>
|
||||
public decimal MultiplierShow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 供应商分组名称
|
||||
/// </summary>
|
||||
public string? ProviderName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型图标URL
|
||||
/// </summary>
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为尊享模型
|
||||
/// </summary>
|
||||
public bool IsPremium { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI模型列表输入
|
||||
/// </summary>
|
||||
public class AiModelGetListInput : PagedAllResultRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 搜索关键词(搜索模型名称、模型ID)
|
||||
/// </summary>
|
||||
public string? SearchKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AI应用ID筛选
|
||||
/// </summary>
|
||||
public Guid? AiAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否只显示尊享模型
|
||||
/// </summary>
|
||||
public bool? IsPremiumOnly { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI模型输入
|
||||
/// </summary>
|
||||
public class AiModelUpdateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型ID不能为空")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 处理名
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "处理名不能为空")]
|
||||
[StringLength(100, ErrorMessage = "处理名不能超过100个字符")]
|
||||
public string HandlerName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型ID不能为空")]
|
||||
[StringLength(200, ErrorMessage = "模型ID不能超过200个字符")]
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型名称不能为空")]
|
||||
[StringLength(200, ErrorMessage = "模型名称不能超过200个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型描述
|
||||
/// </summary>
|
||||
[StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")]
|
||||
public int OrderNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AI应用ID
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "AI应用ID不能为空")]
|
||||
public Guid AiAppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外信息
|
||||
/// </summary>
|
||||
[StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")]
|
||||
public string? ExtraInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型类型不能为空")]
|
||||
public ModelTypeEnum ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型API类型
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "模型API类型不能为空")]
|
||||
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型倍率
|
||||
/// </summary>
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")]
|
||||
public decimal Multiplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型显示倍率
|
||||
/// </summary>
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")]
|
||||
public decimal MultiplierShow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 供应商分组名称
|
||||
/// </summary>
|
||||
[StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")]
|
||||
public string? ProviderName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型图标URL
|
||||
/// </summary>
|
||||
[StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为尊享模型
|
||||
/// </summary>
|
||||
public bool IsPremium { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
public class AgentResultOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public AgentResultTypeEnum TypeEnum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
public string Type => TypeEnum.GetJsonName();
|
||||
|
||||
/// <summary>
|
||||
/// 内容载体
|
||||
/// </summary>
|
||||
public object Content { get; set; }
|
||||
}
|
||||
|
||||
public enum AgentResultTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// 文本内容
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
Text,
|
||||
/// <summary>
|
||||
/// 工具调用中
|
||||
/// </summary>
|
||||
[JsonPropertyName("toolCalling")]
|
||||
ToolCalling,
|
||||
|
||||
/// <summary>
|
||||
/// 工具调用完成
|
||||
/// </summary>
|
||||
[JsonPropertyName("toolCalled")]
|
||||
ToolCalled,
|
||||
|
||||
/// <summary>
|
||||
/// 用量
|
||||
/// </summary>
|
||||
[JsonPropertyName("usage")]
|
||||
Usage,
|
||||
|
||||
/// <summary>
|
||||
/// 工具调用用量
|
||||
/// </summary>
|
||||
[JsonPropertyName("toolCallUsage")]
|
||||
ToolCallUsage
|
||||
}
|
||||
|
||||
public static class AgentResultTypeEnumExtensions
|
||||
{
|
||||
public static string GetJsonName(this AgentResultTypeEnum value)
|
||||
{
|
||||
var member = typeof(AgentResultTypeEnum).GetMember(value.ToString()).FirstOrDefault();
|
||||
var attr = member?.GetCustomAttribute<JsonPropertyNameAttribute>();
|
||||
return attr?.Name ?? value.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
public class AgentSendInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话id
|
||||
/// </summary>
|
||||
public Guid SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户内容
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// api密钥Id
|
||||
/// </summary>
|
||||
public string Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型id
|
||||
/// </summary>
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 已选择工具
|
||||
/// </summary>
|
||||
public List<string> Tools { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
public class AgentToolOutput
|
||||
{
|
||||
public string Code { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成输入
|
||||
/// </summary>
|
||||
public class ImageGenerationInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 提示词
|
||||
/// </summary>
|
||||
public string Prompt { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public string ModelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 参考图Base64列表(可选,包含前缀如 data:image/png;base64,...)
|
||||
/// </summary>
|
||||
public List<string>? ReferenceImagesBase64 { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// 图片任务输出
|
||||
/// </summary>
|
||||
public class ImageTaskOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提示词
|
||||
/// </summary>
|
||||
public string Prompt { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 参考图Base64列表
|
||||
/// </summary>
|
||||
public List<string>? ReferenceImagesBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 参考图URL列表
|
||||
/// </summary>
|
||||
public List<string>? ReferenceImagesUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成图片Base64(包含前缀)
|
||||
/// </summary>
|
||||
public string? StoreBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成图片URL
|
||||
/// </summary>
|
||||
public string? StoreUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态
|
||||
/// </summary>
|
||||
public TaskStatusEnum TaskStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// 图片任务分页查询输入
|
||||
/// </summary>
|
||||
public class ImageTaskPageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 页码(从1开始)
|
||||
/// </summary>
|
||||
public int PageIndex { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页数量
|
||||
/// </summary>
|
||||
public int PageSize { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态筛选(可选)
|
||||
/// </summary>
|
||||
public TaskStatusEnum? TaskStatus { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
|
||||
|
||||
/// <summary>
|
||||
/// 领取任务奖励输入
|
||||
/// </summary>
|
||||
public class ClaimTaskRewardInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务等级(1=1000w任务,2=3000w任务)
|
||||
/// </summary>
|
||||
public int TaskLevel { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
|
||||
|
||||
/// <summary>
|
||||
/// 每日任务状态输出
|
||||
/// </summary>
|
||||
public class DailyTaskStatusOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 今日消耗的尊享包Token数
|
||||
/// </summary>
|
||||
public long TodayConsumedTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务列表
|
||||
/// </summary>
|
||||
public List<DailyTaskItem> Tasks { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每日任务项
|
||||
/// </summary>
|
||||
public class DailyTaskItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务等级(1=1000w任务,2=3000w任务)
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 任务描述
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 任务要求的Token消耗量
|
||||
/// </summary>
|
||||
public long RequiredTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 奖励的Token数量
|
||||
/// </summary>
|
||||
public long RewardTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态:0=未完成,1=可领取,2=已领取
|
||||
/// </summary>
|
||||
public int Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务进度百分比(0-100)
|
||||
/// </summary>
|
||||
public decimal Progress { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.FileMaster;
|
||||
|
||||
public class VerifyNextInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件数
|
||||
/// </summary>
|
||||
public int FileCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件夹数
|
||||
/// </summary>
|
||||
public int DirectoryCount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
|
||||
|
||||
public class QrCodeOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// Qrcode url
|
||||
/// </summary>
|
||||
public string QrCodeUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 场景值
|
||||
/// </summary>
|
||||
public string Scene { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
|
||||
|
||||
public class QrCodeResultOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回状态
|
||||
/// </summary>
|
||||
public SceneResultEnum SceneResult { get; set; } = SceneResultEnum.Wait;
|
||||
|
||||
/// <summary>
|
||||
/// 如果是已登录,返回token
|
||||
/// </summary>
|
||||
public string? Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 刷新token
|
||||
/// </summary>
|
||||
public string? RefreshToken { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
|
||||
|
||||
public class SceneCacheDto
|
||||
{
|
||||
public SceneResultEnum SceneResult { get; set; } = SceneResultEnum.Wait;
|
||||
|
||||
public SceneTypeEnum SceneType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 如果是绑定类型,需要用户id
|
||||
/// </summary>
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class MessageDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public Guid SessionId { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string ModelId { get; set; }
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class MessageGetListInput:PagedAllResultRequestDto
|
||||
{
|
||||
[Required]
|
||||
public Guid SessionId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||
|
||||
/// <summary>
|
||||
/// API类型选项
|
||||
/// </summary>
|
||||
public class ModelApiTypeOption
|
||||
{
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 枚举值
|
||||
/// </summary>
|
||||
public int Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Extensions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 模型库展示数据
|
||||
/// </summary>
|
||||
public class ModelLibraryDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型描述
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
public ModelTypeEnum ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型名称
|
||||
/// </summary>
|
||||
public string ModelTypeName => ModelType.GetDescription();
|
||||
|
||||
/// <summary>
|
||||
/// 模型支持的API类型
|
||||
/// </summary>
|
||||
public List<ModelApiTypeOutput> ModelApiTypes { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模型显示倍率
|
||||
/// </summary>
|
||||
public decimal MultiplierShow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 供应商分组名称
|
||||
/// </summary>
|
||||
public string? ProviderName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型图标URL
|
||||
/// </summary>
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为尊享模型(PremiumChat类型)
|
||||
/// </summary>
|
||||
public bool IsPremium { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int OrderNum { get; set; }
|
||||
}
|
||||
|
||||
public class ModelApiTypeOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
public ModelApiTypeEnum ModelApiType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型名称
|
||||
/// </summary>
|
||||
public string ModelApiTypeName => ModelApiType.GetDescription();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型库列表查询参数
|
||||
/// </summary>
|
||||
public class ModelLibraryGetListInput : PagedAllResultRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 搜索关键词(搜索模型名称、模型ID)
|
||||
/// </summary>
|
||||
public string? SearchKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 供应商名称筛选
|
||||
/// </summary>
|
||||
public List<string>? ProviderNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型筛选
|
||||
/// </summary>
|
||||
public List<ModelTypeEnum>? ModelTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API类型筛选
|
||||
/// </summary>
|
||||
public List<ModelApiTypeEnum>? ModelApiTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否只显示尊享模型
|
||||
/// </summary>
|
||||
public bool? IsPremiumOnly { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型选项
|
||||
/// </summary>
|
||||
public class ModelTypeOption
|
||||
{
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 枚举值
|
||||
/// </summary>
|
||||
public int Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class ModelGetListOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型分类
|
||||
/// </summary>
|
||||
public string Category { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模型id
|
||||
/// </summary>
|
||||
public string ModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string ModelName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型描述
|
||||
/// </summary>
|
||||
public string? ModelDescribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型价格
|
||||
/// </summary>
|
||||
public double ModelPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型类型
|
||||
/// </summary>
|
||||
public string ModelType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型展示状态
|
||||
/// </summary>
|
||||
public string ModelShow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统提示
|
||||
/// </summary>
|
||||
public string SystemPrompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API 主机地址
|
||||
/// </summary>
|
||||
public string ApiHost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// API 密钥
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注信息
|
||||
/// </summary>
|
||||
public string? Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为尊享包
|
||||
/// </summary>
|
||||
public bool IsPremiumPackage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 创建订单输入DTO
|
||||
/// </summary>
|
||||
public class CreateOrderInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
[Required]
|
||||
public GoodsTypeEnum GoodsType { get; set; }
|
||||
|
||||
public string? ReturnUrl{ get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 创建订单输出DTO
|
||||
/// </summary>
|
||||
public class CreateOrderOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 订单ID
|
||||
/// </summary>
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商家订单号
|
||||
/// </summary>
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付页面HTML内容
|
||||
/// </summary>
|
||||
public object PaymentPageHtml { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 获取商品列表输入DTO
|
||||
/// </summary>
|
||||
public class GetGoodsListInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品类别(可选)
|
||||
/// 如果不传,则返回所有商品
|
||||
/// 如果传了,则只返回指定类别的商品(VIP服务或尊享包)
|
||||
/// </summary>
|
||||
public GoodsCategoryType? GoodsCategoryType { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 商品列表输出DTO
|
||||
/// </summary>
|
||||
public class GoodsListOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品名称
|
||||
/// </summary>
|
||||
public string GoodsName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品原价
|
||||
/// </summary>
|
||||
public decimal OriginalPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品参考价格
|
||||
/// </summary>
|
||||
public decimal ReferencePrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品实际价格(折扣后的价格)
|
||||
/// </summary>
|
||||
public decimal GoodsPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 折扣金额(仅尊享包)
|
||||
/// </summary>
|
||||
public decimal? DiscountAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品类别
|
||||
/// </summary>
|
||||
public string GoodsCategory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 折扣说明(仅尊享包)
|
||||
/// </summary>
|
||||
public string? DiscountDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public GoodsTypeEnum GoodsType { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 查询订单状态输入DTO
|
||||
/// </summary>
|
||||
public class QueryOrderStatusInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 商家订单号
|
||||
/// </summary>
|
||||
public string OutTradeNo { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
/// <summary>
|
||||
/// 查询订单状态输出DTO
|
||||
/// </summary>
|
||||
public class QueryOrderStatusOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 订单ID
|
||||
/// </summary>
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商家订单号
|
||||
/// </summary>
|
||||
public string OutTradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝交易号
|
||||
/// </summary>
|
||||
public string? TradeNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易状态
|
||||
/// </summary>
|
||||
public TradeStatusEnum TradeStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易状态描述
|
||||
/// </summary>
|
||||
public string TradeStatusDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单金额
|
||||
/// </summary>
|
||||
public decimal TotalAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品名称
|
||||
/// </summary>
|
||||
public string GoodsName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品类型
|
||||
/// </summary>
|
||||
public GoodsTypeEnum GoodsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后修改时间
|
||||
/// </summary>
|
||||
public DateTime? LastModificationTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge;
|
||||
|
||||
public class RechargeCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户ID
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 充值金额
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "充值金额必须大于0")]
|
||||
public decimal RechargeAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 充值内容
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(500, ErrorMessage = "充值内容不能超过500个字符")]
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// VIP月数(为空或0表示永久VIP,1个月按31天计算)
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "月数必须大于等于0")]
|
||||
public int? Months { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// VIP天数(为空或0表示不使用天数充值)
|
||||
/// </summary>
|
||||
[Range(0, int.MaxValue, ErrorMessage = "天数必须大于等于0")]
|
||||
public int? Days { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
[StringLength(1000, ErrorMessage = "备注不能超过1000个字符")]
|
||||
public string? Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
[StringLength(200, ErrorMessage = "联系方式不能超过200个字符")]
|
||||
public string? ContactInfo { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge;
|
||||
|
||||
public class RechargeGetListOutput
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 充值金额
|
||||
/// </summary>
|
||||
public decimal RechargeAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string? Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 充值内容
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到期时间
|
||||
/// </summary>
|
||||
public DateTime? ExpireDateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系方式
|
||||
/// </summary>
|
||||
public string? ContactInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class SendMessageInput
|
||||
{
|
||||
public List<Message> Messages { get; set; }
|
||||
public string Model { get; set; }
|
||||
|
||||
public Guid? SessionId{ get; set; }
|
||||
}
|
||||
|
||||
public class Message
|
||||
{
|
||||
public string Role { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class SendMessageStreamOutputDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一标识符
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对象类型
|
||||
/// </summary>
|
||||
public string Object { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间,Unix时间戳格式
|
||||
/// </summary>
|
||||
public long Created { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选择项列表
|
||||
/// </summary>
|
||||
public List<Choice> Choices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统指纹(可能为空)
|
||||
/// </summary>
|
||||
public string SystemFingerprint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 使用情况信息
|
||||
/// </summary>
|
||||
public Usage Usage { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选择项类,表示模型返回的一个选择
|
||||
/// </summary>
|
||||
public class Choice
|
||||
{
|
||||
/// <summary>
|
||||
/// 选择索引
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变化内容,包括内容字符串和角色
|
||||
/// </summary>
|
||||
public Delta Delta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束原因,可能为空
|
||||
/// </summary>
|
||||
public string? FinishReason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内容过滤结果
|
||||
/// </summary>
|
||||
public ContentFilterResults ContentFilterResults { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 变化内容
|
||||
/// </summary>
|
||||
public class Delta
|
||||
{
|
||||
/// <summary>
|
||||
/// 内容文本
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色,例如"assistant"
|
||||
/// </summary>
|
||||
public string Role { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内容过滤结果
|
||||
/// </summary>
|
||||
public class ContentFilterResults
|
||||
{
|
||||
public FilterStatus Hate { get; set; }
|
||||
public FilterStatus SelfHarm { get; set; }
|
||||
public FilterStatus Sexual { get; set; }
|
||||
public FilterStatus Violence { get; set; }
|
||||
public FilterStatus Jailbreak { get; set; }
|
||||
public FilterStatus Profanity { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 过滤状态,表示是否经过过滤以及检测是否命中
|
||||
/// </summary>
|
||||
public class FilterStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否被过滤
|
||||
/// </summary>
|
||||
public bool Filtered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否检测到该类型(例如 Jailbreak 中存在此字段)
|
||||
/// </summary>
|
||||
public bool? Detected { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用情况,记录 token 数量等信息
|
||||
/// </summary>
|
||||
public class Usage
|
||||
{
|
||||
/// <summary>
|
||||
/// 提示词数量
|
||||
/// </summary>
|
||||
public int PromptTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 补全词数量
|
||||
/// </summary>
|
||||
public int CompletionTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总的 Token 数量
|
||||
/// </summary>
|
||||
public int TotalTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提示词详细信息
|
||||
/// </summary>
|
||||
public PromptTokensDetails PromptTokensDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 补全文字详细信息
|
||||
/// </summary>
|
||||
public CompletionTokensDetails CompletionTokensDetails { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提示词相关 token 详细信息
|
||||
/// </summary>
|
||||
public class PromptTokensDetails
|
||||
{
|
||||
public int AudioTokens { get; set; }
|
||||
public int CachedTokens { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 补全相关 token 详细信息
|
||||
/// </summary>
|
||||
public class CompletionTokensDetails
|
||||
{
|
||||
public int AudioTokens { get; set; }
|
||||
|
||||
public int ReasoningTokens { get; set; }
|
||||
|
||||
public int AcceptedPredictionTokens { get; set; }
|
||||
|
||||
public int RejectedPredictionTokens { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class SessionCreateAndUpdateInput
|
||||
{
|
||||
public string SessionTitle { get; set; }
|
||||
public string SessionContent { get; set; }
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class SessionDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public string SessionTitle { get; set; }
|
||||
public string SessionContent { get; set; }
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
|
||||
public class SessionGetListInput:PagedAllResultRequestDto
|
||||
{
|
||||
public string? SessionTitle { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||
|
||||
/// <summary>
|
||||
/// 创建Token输入
|
||||
/// </summary>
|
||||
public class TokenCreateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称(同一用户不能重复)
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "名称不能为空")]
|
||||
[StringLength(100, ErrorMessage = "名称长度不能超过100个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间(空为永不过期)
|
||||
/// </summary>
|
||||
public DateTime? ExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 尊享包额度限制(空为不限制)
|
||||
/// </summary>
|
||||
public long? PremiumQuotaLimit { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||
|
||||
/// <summary>
|
||||
/// Token列表输出
|
||||
/// </summary>
|
||||
public class TokenGetListOutputDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Token Id
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token密钥
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间(空为永不过期)
|
||||
/// </summary>
|
||||
public DateTime? ExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 尊享包额度限制(空为不限制)
|
||||
/// </summary>
|
||||
public long? PremiumQuotaLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 尊享包已使用额度
|
||||
/// </summary>
|
||||
public long PremiumUsedQuota { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否禁用
|
||||
/// </summary>
|
||||
public bool IsDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreationTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||
|
||||
public class TokenOutput
|
||||
{
|
||||
public string? ApiKey { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||
|
||||
public class TokenSelectListOutputDto
|
||||
{
|
||||
public Guid TokenId { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsDisabled { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Token;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑Token输入
|
||||
/// </summary>
|
||||
public class TokenUpdateInput
|
||||
{
|
||||
/// <summary>
|
||||
/// Token Id
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Id不能为空")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称(同一用户不能重复)
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "名称不能为空")]
|
||||
[StringLength(100, ErrorMessage = "名称长度不能超过100个字符")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间(空为永不过期)
|
||||
/// </summary>
|
||||
public DateTime? ExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 尊享包额度限制(空为不限制)
|
||||
/// </summary>
|
||||
public long? PremiumQuotaLimit { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
/// <summary>
|
||||
/// 每日Token使用量统计DTO
|
||||
/// </summary>
|
||||
public class DailyTokenUsageDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 日期
|
||||
/// </summary>
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token消耗量
|
||||
/// </summary>
|
||||
public long Tokens { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
/// <summary>
|
||||
/// 模型Token使用量统计DTO
|
||||
/// </summary>
|
||||
public class ModelTokenUsageDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public string Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总消耗量
|
||||
/// </summary>
|
||||
public long Tokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 占比(百分比)
|
||||
/// </summary>
|
||||
public decimal Percentage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
/// <summary>
|
||||
/// 尊享服务Token用量统计DTO
|
||||
/// </summary>
|
||||
public class PremiumTokenUsageDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 总Token数
|
||||
/// </summary>
|
||||
public long PremiumTotalTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 已使用Token数
|
||||
/// </summary>
|
||||
public long PremiumUsedTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余Token数
|
||||
/// </summary>
|
||||
public long PremiumRemainingTokens { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
public class PremiumTokenUsageGetListInput : PagedAllResultRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否免费
|
||||
/// </summary>
|
||||
public bool? IsFree { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
public class PremiumTokenUsageGetListOutput : CreationAuditedEntityDto
|
||||
{
|
||||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户ID
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 包名称
|
||||
/// </summary>
|
||||
public string PackageName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总用量(总token数)
|
||||
/// </summary>
|
||||
public long TotalTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余用量(剩余token数)
|
||||
/// </summary>
|
||||
public long RemainingTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 已使用token数
|
||||
/// </summary>
|
||||
public long UsedTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 到期时间
|
||||
/// </summary>
|
||||
public DateTime? ExpireDateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否激活
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 购买金额
|
||||
/// </summary>
|
||||
public decimal PurchaseAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string? Remark { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
/// <summary>
|
||||
/// 尊享包不同Token用量占比DTO(饼图)
|
||||
/// </summary>
|
||||
public class TokenPremiumUsageDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Token Id
|
||||
/// </summary>
|
||||
public Guid TokenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token名称
|
||||
/// </summary>
|
||||
public string TokenName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Token消耗量
|
||||
/// </summary>
|
||||
public long Tokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 占比(百分比)
|
||||
/// </summary>
|
||||
public decimal Percentage { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
public class UsageStatisticsGetInput
|
||||
{
|
||||
/// <summary>
|
||||
/// tokenId
|
||||
/// </summary>
|
||||
public Guid? TokenId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Volo.Abp.Application.Services;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 激活码服务接口
|
||||
/// </summary>
|
||||
public interface IActivationCodeService : IApplicationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 批量生成激活码
|
||||
/// </summary>
|
||||
Task<ActivationCodeCreateListOutput> CreateBatchAsync(ActivationCodeCreateListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 兑换激活码
|
||||
/// </summary>
|
||||
Task<ActivationCodeRedeemOutput> RedeemAsync(ActivationCodeRedeemInput input);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Announcement;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 公告服务接口
|
||||
/// </summary>
|
||||
public interface IAnnouncementService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取公告信息
|
||||
/// </summary>
|
||||
/// <returns>公告信息</returns>
|
||||
Task<List<AnnouncementLogDto>> GetAsync();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌服务接口
|
||||
/// </summary>
|
||||
public interface ICardFlipService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取本周翻牌任务状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<CardFlipStatusOutput> GetWeeklyTaskStatusAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌
|
||||
/// </summary>
|
||||
/// <param name="input">翻牌输入</param>
|
||||
/// <returns></returns>
|
||||
Task<FlipCardOutput> FlipCardAsync(FlipCardInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 使用邀请码解锁翻牌次数
|
||||
/// </summary>
|
||||
/// <param name="input">邀请码输入</param>
|
||||
/// <returns></returns>
|
||||
Task UseInviteCodeAsync(UseInviteCodeInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取我的邀请码信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<InviteCodeOutput> GetMyInviteCodeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 生成我的邀请码(如果没有)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> GenerateMyInviteCodeAsync();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 渠道商管理服务接口
|
||||
/// </summary>
|
||||
public interface IChannelService
|
||||
{
|
||||
#region AI应用管理
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI应用列表
|
||||
/// </summary>
|
||||
/// <param name="input">查询参数</param>
|
||||
/// <returns>分页应用列表</returns>
|
||||
Task<PagedResultDto<AiAppDto>> GetAppListAsync(AiAppGetListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取AI应用
|
||||
/// </summary>
|
||||
/// <param name="id">应用ID</param>
|
||||
/// <returns>应用详情</returns>
|
||||
Task<AiAppDto> GetAppByIdAsync(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI应用
|
||||
/// </summary>
|
||||
/// <param name="input">创建输入</param>
|
||||
/// <returns>创建的应用</returns>
|
||||
Task<AiAppDto> CreateAppAsync(AiAppCreateInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI应用
|
||||
/// </summary>
|
||||
/// <param name="input">更新输入</param>
|
||||
/// <returns>更新后的应用</returns>
|
||||
Task<AiAppDto> UpdateAppAsync(AiAppUpdateInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 删除AI应用
|
||||
/// </summary>
|
||||
/// <param name="id">应用ID</param>
|
||||
Task DeleteAppAsync(Guid id);
|
||||
|
||||
#endregion
|
||||
|
||||
#region AI模型管理
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI模型列表
|
||||
/// </summary>
|
||||
/// <param name="input">查询参数</param>
|
||||
/// <returns>分页模型列表</returns>
|
||||
Task<PagedResultDto<AiModelDto>> GetModelListAsync(AiModelGetListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取AI模型
|
||||
/// </summary>
|
||||
/// <param name="id">模型ID</param>
|
||||
/// <returns>模型详情</returns>
|
||||
Task<AiModelDto> GetModelByIdAsync(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI模型
|
||||
/// </summary>
|
||||
/// <param name="input">创建输入</param>
|
||||
/// <returns>创建的模型</returns>
|
||||
Task<AiModelDto> CreateModelAsync(AiModelCreateInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI模型
|
||||
/// </summary>
|
||||
/// <param name="input">更新输入</param>
|
||||
/// <returns>更新后的模型</returns>
|
||||
Task<AiModelDto> UpdateModelAsync(AiModelUpdateInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 删除AI模型(软删除)
|
||||
/// </summary>
|
||||
/// <param name="id">模型ID</param>
|
||||
Task DeleteModelAsync(Guid id);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Model;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 模型服务接口
|
||||
/// </summary>
|
||||
public interface IModelService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取模型库列表(公开接口,无需登录)
|
||||
/// </summary>
|
||||
/// <param name="input">查询参数</param>
|
||||
/// <returns>分页模型列表</returns>
|
||||
Task<PagedResultDto<ModelLibraryDto>> GetListAsync(ModelLibraryGetListInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取供应商列表(公开接口,无需登录)
|
||||
/// </summary>
|
||||
/// <returns>供应商列表</returns>
|
||||
Task<List<string>> GetProviderListAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型类型选项列表(公开接口,无需登录)
|
||||
/// </summary>
|
||||
/// <returns>模型类型选项</returns>
|
||||
Task<List<ModelTypeOption>> GetModelTypeOptionsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取API类型选项列表(公开接口,无需登录)
|
||||
/// </summary>
|
||||
/// <returns>API类型选项</returns>
|
||||
Task<List<ModelApiTypeOption>> GetApiTypeOptionsAsync();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Pay;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 支付服务接口
|
||||
/// </summary>
|
||||
public interface IPayService : IApplicationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建订单并发起支付
|
||||
/// </summary>
|
||||
/// <param name="input">创建订单输入</param>
|
||||
/// <returns>订单创建结果</returns>
|
||||
Task<CreateOrderOutput> CreateOrderAsync(CreateOrderInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝异步通知处理
|
||||
/// </summary>
|
||||
/// <param name="form">表单数据</param>
|
||||
/// <returns></returns>
|
||||
Task<string> AlipayNotifyAsync([FromForm] IFormCollection form);
|
||||
|
||||
/// <summary>
|
||||
/// 查询订单状态
|
||||
/// </summary>
|
||||
/// <param name="input">查询订单状态输入</param>
|
||||
/// <returns>订单状态信息</returns>
|
||||
Task<QueryOrderStatusOutput> QueryOrderStatusAsync([FromQuery] QueryOrderStatusInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取商品列表
|
||||
/// </summary>
|
||||
/// <param name="input">获取商品列表输入</param>
|
||||
/// <returns>商品列表</returns>
|
||||
Task<List<GoodsListOutput>> GetGoodsListAsync([FromQuery] GetGoodsListInput input);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
public interface IRechargeService
|
||||
{
|
||||
/// <summary>
|
||||
/// 移除用户vip及角色
|
||||
/// </summary>
|
||||
Task RemoveVipRoleByExpireAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 给用户充值VIP
|
||||
/// </summary>
|
||||
/// <param name="input">充值输入参数</param>
|
||||
/// <returns></returns>
|
||||
Task RechargeVipAsync(RechargeCreateInput input);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.UsageStatistics;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
|
||||
/// <summary>
|
||||
/// 使用量统计服务接口
|
||||
/// </summary>
|
||||
public interface IUsageStatisticsService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前用户近7天的Token消耗统计
|
||||
/// </summary>
|
||||
/// <returns>每日Token使用量列表</returns>
|
||||
Task<List<DailyTokenUsageDto>> GetLast7DaysTokenUsageAsync(UsageStatisticsGetInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户各个模型的Token消耗量及占比
|
||||
/// </summary>
|
||||
/// <returns>模型Token使用量列表</returns>
|
||||
Task<List<ModelTokenUsageDto>> GetModelTokenUsageAsync(UsageStatisticsGetInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前用户尊享服务Token用量统计
|
||||
/// </summary>
|
||||
/// <returns>尊享服务Token用量统计</returns>
|
||||
Task<PremiumTokenUsageDto> GetPremiumTokenUsageAsync();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\common.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\framework\Yi.Framework.Ddd.Application.Contracts\Yi.Framework.Ddd.Application.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\rbac\Yi.Framework.Rbac.Application.Contracts\Yi.Framework.Rbac.Application.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.AiHub.Domain.Shared\Yi.Framework.AiHub.Domain.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yi.Framework.AiHub.Domain.Shared;
|
||||
using Yi.Framework.Ddd.Application.Contracts;
|
||||
using Yi.Framework.Rbac.Application.Contracts;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Contracts
|
||||
{
|
||||
[DependsOn(
|
||||
typeof(YiFrameworkAiHubDomainSharedModule),
|
||||
typeof(YiFrameworkDddApplicationContractsModule),
|
||||
|
||||
typeof(YiFrameworkRbacApplicationContractsModule)
|
||||
)]
|
||||
public class YiFrameworkAiHubApplicationContractsModule : AbpModule
|
||||
{
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
var build = context.Services.GetConfiguration();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Volo.Abp.BackgroundJobs;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成后台任务
|
||||
/// </summary>
|
||||
public class ImageGenerationJob : AsyncBackgroundJob<ImageGenerationJobArgs>, ITransientDependency
|
||||
{
|
||||
private readonly ILogger<ImageGenerationJob> _logger;
|
||||
private readonly AiGateWayManager _aiGateWayManager;
|
||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageStoreTaskRepository;
|
||||
|
||||
public ImageGenerationJob(
|
||||
ILogger<ImageGenerationJob> logger,
|
||||
AiGateWayManager aiGateWayManager,
|
||||
ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageStoreTaskRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_aiGateWayManager = aiGateWayManager;
|
||||
_imageStoreTaskRepository = imageStoreTaskRepository;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(ImageGenerationJobArgs args)
|
||||
{
|
||||
_logger.LogInformation("开始执行图片生成任务,TaskId: {TaskId}, ModelId: {ModelId}, UserId: {UserId}",
|
||||
args.TaskId, args.ModelId, args.UserId);
|
||||
|
||||
try
|
||||
{
|
||||
var request = JsonSerializer.Deserialize<JsonElement>(args.RequestJson);
|
||||
|
||||
await _aiGateWayManager.GeminiGenerateContentImageForStatisticsAsync(
|
||||
args.TaskId,
|
||||
args.ModelId,
|
||||
request,
|
||||
args.UserId);
|
||||
|
||||
_logger.LogInformation("图片生成任务完成,TaskId: {TaskId}", args.TaskId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "图片生成任务失败,TaskId: {TaskId}, Error: {Error}", args.TaskId, ex.Message);
|
||||
|
||||
// 更新任务状态为失败
|
||||
var task = await _imageStoreTaskRepository.GetFirstAsync(x => x.Id == args.TaskId);
|
||||
if (task != null)
|
||||
{
|
||||
task.TaskStatus = TaskStatusEnum.Fail;
|
||||
await _imageStoreTaskRepository.UpdateAsync(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Yi.Framework.AiHub.Application.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成后台任务参数
|
||||
/// </summary>
|
||||
public class ImageGenerationJobArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 图片任务ID
|
||||
/// </summary>
|
||||
public Guid TaskId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型ID
|
||||
/// </summary>
|
||||
public string ModelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 请求JSON字符串
|
||||
/// </summary>
|
||||
public string RequestJson { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 用户ID
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using Medallion.Threading;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.ActivationCode;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Recharge;
|
||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 激活码服务
|
||||
/// </summary>
|
||||
public class ActivationCodeService : ApplicationService, IActivationCodeService
|
||||
{
|
||||
private readonly ActivationCodeManager _activationCodeManager;
|
||||
private readonly IRechargeService _rechargeService;
|
||||
private readonly PremiumPackageManager _premiumPackageManager;
|
||||
private IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
|
||||
public ActivationCodeService(
|
||||
ActivationCodeManager activationCodeManager,
|
||||
IRechargeService rechargeService,
|
||||
PremiumPackageManager premiumPackageManager)
|
||||
{
|
||||
_activationCodeManager = activationCodeManager;
|
||||
_rechargeService = rechargeService;
|
||||
_premiumPackageManager = premiumPackageManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量生成激活码
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost("activationCode/Batch")]
|
||||
public async Task<ActivationCodeCreateListOutput> CreateBatchAsync(ActivationCodeCreateListInput input)
|
||||
{
|
||||
if (input.Items == null || input.Items.Count == 0)
|
||||
{
|
||||
throw new UserFriendlyException("生成列表不能为空");
|
||||
}
|
||||
|
||||
var entities = await _activationCodeManager.CreateBatchAsync(
|
||||
input.Items.Select(x => (x.GoodsType, x.Count)).ToList());
|
||||
|
||||
var outputs = entities
|
||||
.GroupBy(x => x.GoodsType)
|
||||
.Select(group => new ActivationCodeCreateOutput
|
||||
{
|
||||
GoodsType = group.Key,
|
||||
Count = group.Count(),
|
||||
Codes = group.Select(x => x.Code).ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return new ActivationCodeCreateListOutput
|
||||
{
|
||||
Items = outputs
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兑换激活码
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost("activationCode/Redeem")]
|
||||
public async Task<ActivationCodeRedeemOutput> RedeemAsync(ActivationCodeRedeemInput input)
|
||||
{
|
||||
//自旋等待,防抖
|
||||
await using var handle =
|
||||
await DistributedLock.AcquireLockAsync($"Yi:AiHub:ActivationCodeLock:{input.Code}");
|
||||
|
||||
var userId = CurrentUser.GetId();
|
||||
var redeemContext = await _activationCodeManager.RedeemAsync(userId, input.Code);
|
||||
var goodsType = redeemContext.ActivationCode.GoodsType;
|
||||
var goods = redeemContext.Goods;
|
||||
var packageName = redeemContext.PackageName;
|
||||
var totalAmount = goods.Price;
|
||||
|
||||
if (goods.TokenAmount > 0)
|
||||
{
|
||||
await _premiumPackageManager.CreatePremiumPackageAsync(
|
||||
userId,
|
||||
goods.TokenAmount,
|
||||
packageName,
|
||||
totalAmount,
|
||||
"激活码兑换",
|
||||
expireMonths: null,
|
||||
isCreateRechargeRecord: !goods.IsCombo);
|
||||
}
|
||||
|
||||
if (goods.VipMonths > 0 || goods.VipDays > 0)
|
||||
{
|
||||
await _rechargeService.RechargeVipAsync(new RechargeCreateInput
|
||||
{
|
||||
UserId = userId,
|
||||
RechargeAmount = totalAmount,
|
||||
Content = packageName,
|
||||
Months = goods.VipMonths,
|
||||
Days = goods.VipDays,
|
||||
Remark = "激活码兑换",
|
||||
ContactInfo = null
|
||||
});
|
||||
}
|
||||
|
||||
return new ActivationCodeRedeemOutput
|
||||
{
|
||||
GoodsType = goodsType,
|
||||
PackageName = packageName,
|
||||
Content = packageName
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip;
|
||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.AiHub.Domain.Extensions;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌服务 - 应用层组合服务
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class CardFlipService : ApplicationService, ICardFlipService
|
||||
{
|
||||
private readonly CardFlipManager _cardFlipManager;
|
||||
private readonly InviteCodeManager _inviteCodeManager;
|
||||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||
private readonly ILogger<CardFlipService> _logger;
|
||||
|
||||
public CardFlipService(
|
||||
CardFlipManager cardFlipManager,
|
||||
InviteCodeManager inviteCodeManager,
|
||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||||
ILogger<CardFlipService> logger)
|
||||
{
|
||||
_cardFlipManager = cardFlipManager;
|
||||
_inviteCodeManager = inviteCodeManager;
|
||||
_premiumPackageRepository = premiumPackageRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取本周翻牌任务状态
|
||||
/// </summary>
|
||||
public async Task<CardFlipStatusOutput> GetWeeklyTaskStatusAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||||
|
||||
// 获取本周任务
|
||||
var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: false);
|
||||
|
||||
// 获取邀请码信息
|
||||
var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId);
|
||||
|
||||
// 统计本周邀请人数
|
||||
var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart);
|
||||
|
||||
//当前用户是否已填写过邀请码
|
||||
var isFilledInviteCode = await _inviteCodeManager.IsFilledInviteCodeAsync(userId);
|
||||
var output = new CardFlipStatusOutput
|
||||
{
|
||||
TotalFlips = task?.TotalFlips ?? 0,
|
||||
RemainingFreeFlips = CardFlipManager.MAX_FREE_FLIPS - (task?.FreeFlipsUsed ?? 0),
|
||||
RemainingBonusFlips = 0, // 已废弃
|
||||
RemainingInviteFlips = CardFlipManager.MAX_INVITE_FLIPS - (task?.InviteFlipsUsed ?? 0),
|
||||
CanFlip = _cardFlipManager.CanFlipCard(task),
|
||||
MyInviteCode = inviteCode?.Code,
|
||||
InvitedCount = invitedCount,
|
||||
FlipRecords = BuildFlipRecords(task),
|
||||
IsFilledInviteCode = isFilledInviteCode
|
||||
};
|
||||
|
||||
// 生成提示信息
|
||||
output.NextFlipTip = GenerateNextFlipTip(output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 翻牌
|
||||
/// </summary>
|
||||
public async Task<FlipCardOutput> FlipCardAsync(FlipCardInput input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||||
|
||||
// 执行翻牌逻辑(由Manager处理验证和翻牌)
|
||||
var result = await _cardFlipManager.ExecuteFlipAsync(userId, input.FlipNumber, weekStart);
|
||||
|
||||
// 如果中奖,发放奖励
|
||||
if (result.IsWin)
|
||||
{
|
||||
await GrantRewardAsync(userId, result.RewardAmount, $"翻牌活动-序号{input.FlipNumber}中奖");
|
||||
}
|
||||
|
||||
// 构建输出
|
||||
var output = new FlipCardOutput
|
||||
{
|
||||
FlipNumber = result.FlipNumber,
|
||||
IsWin = result.IsWin,
|
||||
RewardAmount = result.RewardAmount,
|
||||
RewardDesc = result.RewardDesc,
|
||||
RemainingFlips = CardFlipManager.TOTAL_MAX_FLIPS - input.FlipNumber
|
||||
};
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用邀请码解锁翻牌次数
|
||||
/// </summary>
|
||||
public async Task UseInviteCodeAsync(UseInviteCodeInput input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||||
|
||||
// 获取本周任务
|
||||
var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true);
|
||||
|
||||
// 验证是否已经使用了所有邀请解锁次数
|
||||
if (task.InviteFlipsUsed >= CardFlipManager.MAX_INVITE_FLIPS)
|
||||
{
|
||||
throw new UserFriendlyException("本周邀请解锁次数已用完");
|
||||
}
|
||||
|
||||
// 使用邀请码(由Manager处理验证和邀请逻辑)
|
||||
await _inviteCodeManager.UseInviteCodeAsync(userId, input.InviteCode);
|
||||
|
||||
_logger.LogInformation($"用户 {userId} 使用邀请码 {input.InviteCode} 解锁翻牌次数成功");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取我的邀请码信息
|
||||
/// </summary>
|
||||
public async Task<InviteCodeOutput> GetMyInviteCodeAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||||
|
||||
// 获取我的邀请码
|
||||
var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId);
|
||||
|
||||
// 统计本周邀请人数
|
||||
var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart);
|
||||
|
||||
// 获取邀请历史
|
||||
var invitationHistory = await _inviteCodeManager.GetInvitationHistoryAsync(userId, 10);
|
||||
|
||||
return new InviteCodeOutput
|
||||
{
|
||||
MyInviteCode = inviteCode?.Code,
|
||||
InvitedCount = invitedCount,
|
||||
InvitationHistory = invitationHistory.Select(x => new InvitationHistoryItem
|
||||
{
|
||||
InvitedUserName = x.InvitedUserName,
|
||||
InvitationTime = x.InvitationTime,
|
||||
WeekDescription = GetWeekDescription(x.InvitationTime)
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成我的邀请码
|
||||
/// </summary>
|
||||
public async Task<string> GenerateMyInviteCodeAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
|
||||
// 生成邀请码(由Manager处理)
|
||||
var code = await _inviteCodeManager.GenerateInviteCodeForUserAsync(userId);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
#region 私有辅助方法
|
||||
|
||||
/// <summary>
|
||||
/// 构建翻牌记录列表
|
||||
/// </summary>
|
||||
private List<CardFlipRecord> BuildFlipRecords(CardFlipTaskAggregateRoot? task)
|
||||
{
|
||||
var records = new List<CardFlipRecord>();
|
||||
|
||||
// 获取已翻牌的顺序
|
||||
var flippedOrder = task != null ? _cardFlipManager.GetFlippedOrder(task) : new List<int>();
|
||||
var flippedNumbers = new HashSet<int>(flippedOrder);
|
||||
|
||||
// 获取中奖记录
|
||||
var winRecords = task?.WinRecords ?? new Dictionary<int, long>();
|
||||
|
||||
// 构建记录,按照原始序号1-10排列
|
||||
for (int i = 1; i <= CardFlipManager.TOTAL_MAX_FLIPS; i++)
|
||||
{
|
||||
var record = new CardFlipRecord
|
||||
{
|
||||
FlipNumber = i,
|
||||
IsFlipped = flippedNumbers.Contains(i),
|
||||
IsWin = false,
|
||||
FlipTypeDesc = CardFlipManager.GetFlipTypeDesc(i),
|
||||
// 设置在翻牌顺序中的位置(0表示未翻,>0表示第几个翻的)
|
||||
FlipOrderIndex = flippedOrder.IndexOf(i) >= 0 ? flippedOrder.IndexOf(i) + 1 : 0
|
||||
};
|
||||
|
||||
// 设置中奖信息
|
||||
// 判断这张卡是第几次翻的
|
||||
if (task != null && flippedNumbers.Contains(i))
|
||||
{
|
||||
var flipOrderIndex = flippedOrder.IndexOf(i) + 1; // 第几次翻的(1-based)
|
||||
|
||||
// 检查这次翻牌是否中奖
|
||||
if (winRecords.TryGetValue(flipOrderIndex, out var rewardAmount))
|
||||
{
|
||||
record.IsWin = true;
|
||||
record.RewardAmount = rewardAmount;
|
||||
}
|
||||
}
|
||||
|
||||
records.Add(record);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成下次翻牌提示
|
||||
/// </summary>
|
||||
private string GenerateNextFlipTip(CardFlipStatusOutput status)
|
||||
{
|
||||
if (status.TotalFlips >= CardFlipManager.TOTAL_MAX_FLIPS)
|
||||
{
|
||||
return "本周翻牌次数已用完,请下周再来!";
|
||||
}
|
||||
|
||||
if (status.RemainingFreeFlips > 0)
|
||||
{
|
||||
return $"本周您还有{status.RemainingFreeFlips}次免费翻牌机会";
|
||||
}
|
||||
else if (status.RemainingInviteFlips > 0)
|
||||
{
|
||||
if (status.TotalFlips >= 7)
|
||||
{
|
||||
return $"本周使用他人邀请码或他人使用你的邀请码,可解锁{status.RemainingInviteFlips}次翻牌,且必中大奖!每次中奖最大额度将翻倍!";
|
||||
}
|
||||
|
||||
return $"本周使用他人邀请码或他人使用你的邀请码,可解锁{status.RemainingInviteFlips}次翻牌,必中大奖!每次中奖最大额度将翻倍!";
|
||||
}
|
||||
|
||||
return "继续加油!";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放奖励
|
||||
/// </summary>
|
||||
private async Task GrantRewardAsync(Guid userId, long amount, string description)
|
||||
{
|
||||
var premiumPackage = new PremiumPackageAggregateRoot(userId, amount, description)
|
||||
{
|
||||
PurchaseAmount = 0, // 奖励不需要付费
|
||||
Remark = $"翻牌活动奖励:{amount / 10000}w tokens"
|
||||
};
|
||||
|
||||
await _premiumPackageRepository.InsertAsync(premiumPackage);
|
||||
|
||||
_logger.LogInformation($"用户 {userId} 获得翻牌奖励 {amount / 10000}w tokens");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取周描述
|
||||
/// </summary>
|
||||
private string GetWeekDescription(DateTime date)
|
||||
{
|
||||
var weekStart = CardFlipManager.GetWeekStartDate(date);
|
||||
var weekEnd = weekStart.AddDays(6);
|
||||
|
||||
return $"{weekStart:MM-dd} 至 {weekEnd:MM-dd}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using Medallion.Threading;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||
using Yi.Framework.AiHub.Domain.Extensions;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 每日任务服务
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class DailyTaskService : ApplicationService
|
||||
{
|
||||
private readonly ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> _dailyTaskRepository;
|
||||
private readonly ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
|
||||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||
private readonly ILogger<DailyTaskService> _logger;
|
||||
private IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
|
||||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||
// 任务配置
|
||||
private readonly Dictionary<int, (long RequiredTokens, long RewardTokens, string Name, string Description)>
|
||||
_taskConfigs = new()
|
||||
{
|
||||
{ 1, (10000000, 1000000, "尊享包1000w token任务", "累积使用尊享包 1000w token") }, // 1000w消耗 -> 100w奖励
|
||||
{ 2, (30000000, 2000000, "尊享包3000w token任务", "累积使用尊享包 3000w token") } // 3000w消耗 -> 200w奖励
|
||||
};
|
||||
|
||||
public DailyTaskService(
|
||||
ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> dailyTaskRepository,
|
||||
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||||
ILogger<DailyTaskService> logger, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||
{
|
||||
_dailyTaskRepository = dailyTaskRepository;
|
||||
_messageRepository = messageRepository;
|
||||
_premiumPackageRepository = premiumPackageRepository;
|
||||
_logger = logger;
|
||||
_aiModelRepository = aiModelRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取今日任务状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<DailyTaskStatusOutput> GetTodayTaskStatusAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var today = DateTime.Today;
|
||||
|
||||
// 1. 统计今日尊享包Token消耗量
|
||||
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
|
||||
|
||||
// 2. 查询今日已领取的任务
|
||||
var claimedTasks = await _dailyTaskRepository._DbQueryable
|
||||
.Where(x => x.UserId == userId && x.TaskDate == today)
|
||||
.Select(x => new { x.TaskLevel, x.IsRewarded })
|
||||
.ToListAsync();
|
||||
|
||||
// 3. 构建任务列表
|
||||
var tasks = new List<DailyTaskItem>();
|
||||
foreach (var (level, config) in _taskConfigs)
|
||||
{
|
||||
var claimed = claimedTasks.FirstOrDefault(x => x.TaskLevel == level);
|
||||
int status;
|
||||
|
||||
if (claimed != null && claimed.IsRewarded)
|
||||
{
|
||||
status = 2; // 已领取
|
||||
}
|
||||
else if (todayConsumed >= config.RequiredTokens)
|
||||
{
|
||||
status = 1; // 可领取
|
||||
}
|
||||
else
|
||||
{
|
||||
status = 0; // 未完成
|
||||
}
|
||||
|
||||
var progress = todayConsumed >= config.RequiredTokens
|
||||
? 100
|
||||
: Math.Round((decimal)todayConsumed / config.RequiredTokens * 100, 2);
|
||||
|
||||
tasks.Add(new DailyTaskItem
|
||||
{
|
||||
Level = level,
|
||||
Name = config.Name,
|
||||
Description = config.Description,
|
||||
RequiredTokens = config.RequiredTokens,
|
||||
RewardTokens = config.RewardTokens,
|
||||
Status = status,
|
||||
Progress = progress
|
||||
});
|
||||
}
|
||||
|
||||
return new DailyTaskStatusOutput
|
||||
{
|
||||
TodayConsumedTokens = todayConsumed,
|
||||
Tasks = tasks
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 领取任务奖励
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ClaimTaskRewardAsync(ClaimTaskRewardInput input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
//自旋等待,防抖
|
||||
await using var handle =
|
||||
await DistributedLock.AcquireLockAsync($"Yi:AiHub:ClaimTaskRewardLock:{userId}");
|
||||
|
||||
|
||||
var today = DateTime.Today;
|
||||
|
||||
// 1. 验证任务等级
|
||||
if (!_taskConfigs.TryGetValue(input.TaskLevel, out var taskConfig))
|
||||
{
|
||||
throw new UserFriendlyException($"无效的任务等级: {input.TaskLevel}");
|
||||
}
|
||||
|
||||
// 2. 检查是否已领取
|
||||
var existingRecord = await _dailyTaskRepository._DbQueryable
|
||||
.Where(x => x.UserId == userId && x.TaskDate == today && x.TaskLevel == input.TaskLevel)
|
||||
.FirstAsync();
|
||||
|
||||
if (existingRecord != null)
|
||||
{
|
||||
throw new UserFriendlyException("今日该任务奖励已领取,请明天再来!");
|
||||
}
|
||||
|
||||
// 3. 验证今日Token消耗是否达标
|
||||
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
|
||||
if (todayConsumed < taskConfig.RequiredTokens)
|
||||
{
|
||||
throw new UserFriendlyException(
|
||||
$"Token消耗未达标!需要 {taskConfig.RequiredTokens / 10000}w,当前 {todayConsumed / 10000}w");
|
||||
}
|
||||
|
||||
// 4. 创建奖励包(使用 PremiumPackageManager)
|
||||
|
||||
var premiumPackage =
|
||||
new PremiumPackageAggregateRoot(userId, taskConfig.RewardTokens, $"每日任务:{taskConfig.Name}")
|
||||
{
|
||||
PurchaseAmount = 0, // 奖励不需要付费
|
||||
Remark = $"{today:yyyy-MM-dd} 每日任务奖励"
|
||||
};
|
||||
|
||||
await _premiumPackageRepository.InsertAsync(premiumPackage);
|
||||
|
||||
// 5. 记录领取记录
|
||||
var record = new DailyTaskRewardRecordAggregateRoot(userId, input.TaskLevel, today, taskConfig.RewardTokens)
|
||||
{
|
||||
Remark = $"完成任务{input.TaskLevel},名称:{taskConfig.Name},消耗 {todayConsumed / 10000}w token"
|
||||
};
|
||||
|
||||
await _dailyTaskRepository.InsertAsync(record);
|
||||
|
||||
_logger.LogInformation(
|
||||
$"用户 {userId} 领取每日任务 {input.TaskLevel} 奖励成功,获得 {taskConfig.RewardTokens / 10000}w tokens");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取今日尊享包Token消耗量
|
||||
/// </summary>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <param name="today">今日日期</param>
|
||||
/// <returns>消耗的Token总数</returns>
|
||||
private async Task<long> GetTodayPremiumTokenConsumptionAsync(Guid userId, DateTime today)
|
||||
{
|
||||
var tomorrow = today.AddDays(1);
|
||||
|
||||
// 查询今日所有使用尊享包模型的消息(role=system 表示消耗)
|
||||
// 先获取所有尊享模型的ModelId列表
|
||||
var premiumModelIds = await _aiModelRepository._DbQueryable
|
||||
.Where(x => x.IsPremium)
|
||||
.Select(x => x.ModelId)
|
||||
.ToListAsync();
|
||||
|
||||
var totalTokens = await _messageRepository._DbQueryable
|
||||
.Where(x => x.UserId == userId)
|
||||
.Where(x => x.Role == "system") // system角色表示实际消耗
|
||||
.Where(x => premiumModelIds.Contains(x.ModelId)) // 尊享包模型
|
||||
.Where(x => x.CreationTime >= today && x.CreationTime < tomorrow)
|
||||
.SumAsync(x => x.TokenUsage.TotalTokenCount);
|
||||
|
||||
return totalTokens;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Dtos;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
using Yi.Framework.AiHub.Domain.Extensions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
public class AiAccountService : ApplicationService
|
||||
{
|
||||
private IAccountService _accountService;
|
||||
private ISqlSugarRepository<AiUserExtraInfoEntity> _userRepository;
|
||||
private ISqlSugarRepository<AiRechargeAggregateRoot> _rechargeRepository;
|
||||
private ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||||
private ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
|
||||
public AiAccountService(
|
||||
IAccountService accountService,
|
||||
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository,
|
||||
ISqlSugarRepository<AiRechargeAggregateRoot> rechargeRepository,
|
||||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository, ISqlSugarRepository<MessageAggregateRoot> messageRepository)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_userRepository = userRepository;
|
||||
_rechargeRepository = rechargeRepository;
|
||||
_premiumPackageRepository = premiumPackageRepository;
|
||||
_messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取ai用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
[HttpGet("account/ai")]
|
||||
public async Task<AiUserRoleMenuDto> GetAsync()
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
var userAccount = await _accountService.GetAsync(null, null, userId: CurrentUser.GetId());
|
||||
var output = userAccount.Adapt<AiUserRoleMenuDto>();
|
||||
|
||||
// 是否绑定服务号
|
||||
output.IsBindFuwuhao = await _userRepository.IsAnyAsync(x => userId == x.UserId);
|
||||
|
||||
// 是否为VIP用户
|
||||
output.IsVip = CurrentUser.IsAiVip();
|
||||
|
||||
// 获取VIP到期时间
|
||||
if (output.IsVip)
|
||||
{
|
||||
var recharges = await _rechargeRepository._DbQueryable
|
||||
.Where(x => x.UserId == userId)
|
||||
.ToListAsync();
|
||||
|
||||
if (recharges.Any())
|
||||
{
|
||||
// 如果有任何一个充值记录的过期时间为null,说明是永久VIP
|
||||
if (recharges.Any(x => !x.ExpireDateTime.HasValue))
|
||||
{
|
||||
output.VipExpireTime = null; // 永久VIP
|
||||
}
|
||||
else
|
||||
{
|
||||
// 取最大的过期时间
|
||||
output.VipExpireTime = recharges
|
||||
.Where(x => x.ExpireDateTime.HasValue)
|
||||
.Max(x => x.ExpireDateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取利润统计数据
|
||||
/// </summary>
|
||||
/// <param name="currentCost">当前成本(RMB)</param>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
[HttpGet("account/profit-statistics")]
|
||||
public async Task<string> GetProfitStatisticsAsync([FromQuery] decimal currentCost)
|
||||
{
|
||||
if (CurrentUser.UserName != "Guo" && CurrentUser.UserName != "cc")
|
||||
{
|
||||
throw new UserFriendlyException("您暂无权限访问");
|
||||
}
|
||||
|
||||
// 1. 获取尊享包总消耗和剩余库存
|
||||
var premiumPackages = await _premiumPackageRepository._DbQueryable.ToListAsync();
|
||||
long totalUsedTokens = premiumPackages.Sum(p => p.UsedTokens);
|
||||
long totalRemainingTokens = premiumPackages.Sum(p => p.RemainingTokens);
|
||||
|
||||
// 2. 计算1亿Token成本
|
||||
decimal costPerHundredMillion = totalUsedTokens > 0
|
||||
? currentCost / (totalUsedTokens / 100000000m)
|
||||
: 0;
|
||||
|
||||
// 3. 计算总成本(剩余+已使用的总成本)
|
||||
long totalTokens = totalUsedTokens + totalRemainingTokens;
|
||||
decimal totalCost = totalTokens > 0
|
||||
? (totalTokens / 100000000m) * costPerHundredMillion
|
||||
: 0;
|
||||
|
||||
// 4. 获取总收益(RechargeType=PremiumPackage的充值金额总和)
|
||||
decimal totalRevenue = await _rechargeRepository._DbQueryable
|
||||
.Where(x => x.RechargeType == Domain.Shared.Enums.RechargeTypeEnum.PremiumPackage)
|
||||
.SumAsync(x => x.RechargeAmount);
|
||||
|
||||
// 5. 计算利润率
|
||||
decimal profitRate = totalCost > 0
|
||||
? (totalRevenue / totalCost - 1) * 100
|
||||
: 0;
|
||||
|
||||
// 6. 按200售价计算成本
|
||||
decimal costAt200Price = totalRevenue > 0
|
||||
? (totalCost / totalRevenue) * 200
|
||||
: 0;
|
||||
|
||||
// 7. 格式化输出
|
||||
var today = DateTime.Now;
|
||||
string dayOfWeek = today.ToString("dddd", new System.Globalization.CultureInfo("zh-CN"));
|
||||
string weekDay = dayOfWeek switch
|
||||
{
|
||||
"星期一" => "周1",
|
||||
"星期二" => "周2",
|
||||
"星期三" => "周3",
|
||||
"星期四" => "周4",
|
||||
"星期五" => "周5",
|
||||
"星期六" => "周6",
|
||||
"星期日" => "周日",
|
||||
_ => dayOfWeek
|
||||
};
|
||||
|
||||
var result = $@"{today:M月d日} {weekDay}
|
||||
尊享包已消耗({totalUsedTokens / 100000000m:F2}亿){totalUsedTokens}
|
||||
尊享包剩余库存({totalRemainingTokens / 100000000m:F2}亿){totalRemainingTokens}
|
||||
当前成本:{currentCost:F2}RMB
|
||||
1亿Token成本:{costPerHundredMillion:F2} RMB=1亿 Token
|
||||
总成本:{totalCost:F2} RMB
|
||||
总收益:{totalRevenue:F2}RMB
|
||||
利润率: {profitRate:F1}%
|
||||
按200售价来算,成本在{costAt200Price:F2}";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public class TokenStatisticsInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 指定日期(当天零点)
|
||||
/// </summary>
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模型Id -> 1亿Token成本(RMB)
|
||||
/// </summary>
|
||||
public Dictionary<string, decimal> ModelCosts { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定日期各模型Token统计
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost("account/token-statistics")]
|
||||
public async Task<string> GetTokenStatisticsAsync([FromBody] TokenStatisticsInput input)
|
||||
{
|
||||
if (CurrentUser.UserName != "Guo" && CurrentUser.UserName != "cc")
|
||||
{
|
||||
throw new UserFriendlyException("您暂无权限访问");
|
||||
}
|
||||
|
||||
if (input.ModelCosts is null || input.ModelCosts.Count == 0)
|
||||
{
|
||||
throw new UserFriendlyException("请提供模型成本配置");
|
||||
}
|
||||
|
||||
var day = input.Date.Date;
|
||||
var nextDay = day.AddDays(1);
|
||||
var modelIds = input.ModelCosts.Keys.ToList();
|
||||
|
||||
var modelStats = await _messageRepository._DbQueryable
|
||||
.Where(x => modelIds.Contains(x.ModelId))
|
||||
.Where(x => x.CreationTime >= day && x.CreationTime < nextDay)
|
||||
.Where(x => x.Role == "system")
|
||||
.GroupBy(x => x.ModelId)
|
||||
.Select(x => new
|
||||
{
|
||||
ModelId = x.ModelId,
|
||||
Tokens = SqlFunc.AggregateSum(x.TokenUsage.TotalTokenCount),
|
||||
Count = SqlFunc.AggregateCount(x.Id)
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var modelStatDict = modelStats.ToDictionary(x => x.ModelId, x => x);
|
||||
|
||||
string weekDay = day.ToString("dddd", new CultureInfo("zh-CN")) switch
|
||||
{
|
||||
"星期一" => "周1",
|
||||
"星期二" => "周2",
|
||||
"星期三" => "周3",
|
||||
"星期四" => "周4",
|
||||
"星期五" => "周5",
|
||||
"星期六" => "周6",
|
||||
"星期日" => "周日",
|
||||
_ => day.ToString("dddd", new CultureInfo("zh-CN"))
|
||||
};
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{day:M月d日} {weekDay}");
|
||||
|
||||
foreach (var kvp in input.ModelCosts)
|
||||
{
|
||||
var modelId = kvp.Key;
|
||||
var cost = kvp.Value;
|
||||
|
||||
modelStatDict.TryGetValue(modelId, out var stat);
|
||||
long tokens = stat?.Tokens ?? 0;
|
||||
long count = stat?.Count ?? 0;
|
||||
|
||||
decimal costPerHundredMillion = tokens > 0
|
||||
? cost / (tokens / 100000000m)
|
||||
: 0;
|
||||
|
||||
decimal tokensInWan = tokens / 10000m;
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"{modelId} 成本:【{cost:F2}RMB】 次数:【{count}次】 token:【{tokensInWan:F0}w】 1亿token成本:【{costPerHundredMillion:F2}RMB】");
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Mapster;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Caching;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Announcement;
|
||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 公告服务
|
||||
/// </summary>
|
||||
public class AnnouncementService : ApplicationService, IAnnouncementService
|
||||
{
|
||||
private readonly ISqlSugarRepository<AnnouncementAggregateRoot> _announcementRepository;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IDistributedCache<AnnouncementCacheDto> _announcementCache;
|
||||
private const string AnnouncementCacheKey = "AiHub:Announcement";
|
||||
|
||||
public AnnouncementService(
|
||||
ISqlSugarRepository<AnnouncementAggregateRoot> announcementRepository,
|
||||
IConfiguration configuration,
|
||||
IDistributedCache<AnnouncementCacheDto> announcementCache)
|
||||
{
|
||||
_announcementRepository = announcementRepository;
|
||||
_configuration = configuration;
|
||||
_announcementCache = announcementCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取公告信息
|
||||
/// </summary>
|
||||
public async Task<List<AnnouncementLogDto>> GetAsync()
|
||||
{
|
||||
// 使用 GetOrAddAsync 从缓存获取或添加数据,缓存1小时
|
||||
var cacheData = await _announcementCache.GetOrAddAsync(
|
||||
AnnouncementCacheKey,
|
||||
async () => await LoadAnnouncementDataAsync(),
|
||||
() => new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
|
||||
}
|
||||
);
|
||||
|
||||
return cacheData?.Logs ?? new List<AnnouncementLogDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库加载公告数据
|
||||
/// </summary>
|
||||
private async Task<AnnouncementCacheDto> LoadAnnouncementDataAsync()
|
||||
{
|
||||
// 查询所有公告日志,按日期降序排列
|
||||
var logs = await _announcementRepository._DbQueryable
|
||||
.OrderByDescending(x => x.StartTime)
|
||||
.ToListAsync();
|
||||
|
||||
// 转换为 DTO
|
||||
var logDtos = logs.Adapt<List<AnnouncementLogDto>>();
|
||||
return new AnnouncementCacheDto
|
||||
{
|
||||
Logs = logDtos
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel;
|
||||
using Yi.Framework.AiHub.Application.Contracts.IServices;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 渠道商管理服务实现
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class ChannelService : ApplicationService, IChannelService
|
||||
{
|
||||
private readonly ISqlSugarRepository<AiAppAggregateRoot, Guid> _appRepository;
|
||||
private readonly ISqlSugarRepository<AiModelEntity, Guid> _modelRepository;
|
||||
|
||||
public ChannelService(
|
||||
ISqlSugarRepository<AiAppAggregateRoot, Guid> appRepository,
|
||||
ISqlSugarRepository<AiModelEntity, Guid> modelRepository)
|
||||
{
|
||||
_appRepository = appRepository;
|
||||
_modelRepository = modelRepository;
|
||||
}
|
||||
|
||||
#region AI应用管理
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI应用列表
|
||||
/// </summary>
|
||||
[HttpGet("channel/app")]
|
||||
public async Task<PagedResultDto<AiAppDto>> GetAppListAsync(AiAppGetListInput input)
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
|
||||
var entities = await _appRepository._DbQueryable
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => x.Name.Contains(input.SearchKey))
|
||||
.OrderByDescending(x => x.OrderNum)
|
||||
.OrderByDescending(x => x.CreationTime)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||
|
||||
var output = entities.Adapt<List<AiAppDto>>();
|
||||
return new PagedResultDto<AiAppDto>(total, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取AI应用
|
||||
/// </summary>
|
||||
[HttpGet("channel/app/{id}")]
|
||||
public async Task<AiAppDto> GetAppByIdAsync([FromRoute]Guid id)
|
||||
{
|
||||
var entity = await _appRepository.GetByIdAsync(id);
|
||||
return entity.Adapt<AiAppDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI应用
|
||||
/// </summary>
|
||||
public async Task<AiAppDto> CreateAppAsync(AiAppCreateInput input)
|
||||
{
|
||||
var entity = new AiAppAggregateRoot
|
||||
{
|
||||
Name = input.Name,
|
||||
Endpoint = input.Endpoint,
|
||||
ExtraUrl = input.ExtraUrl,
|
||||
ApiKey = input.ApiKey,
|
||||
OrderNum = input.OrderNum
|
||||
};
|
||||
|
||||
await _appRepository.InsertAsync(entity);
|
||||
return entity.Adapt<AiAppDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI应用
|
||||
/// </summary>
|
||||
public async Task<AiAppDto> UpdateAppAsync(AiAppUpdateInput input)
|
||||
{
|
||||
var entity = await _appRepository.GetByIdAsync(input.Id);
|
||||
|
||||
entity.Name = input.Name;
|
||||
entity.Endpoint = input.Endpoint;
|
||||
entity.ExtraUrl = input.ExtraUrl;
|
||||
entity.ApiKey = input.ApiKey;
|
||||
entity.OrderNum = input.OrderNum;
|
||||
|
||||
await _appRepository.UpdateAsync(entity);
|
||||
return entity.Adapt<AiAppDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除AI应用
|
||||
/// </summary>
|
||||
[HttpDelete("channel/app/{id}")]
|
||||
public async Task DeleteAppAsync([FromRoute]Guid id)
|
||||
{
|
||||
// 检查是否有关联的模型
|
||||
var hasModels = await _modelRepository._DbQueryable
|
||||
.Where(x => x.AiAppId == id && !x.IsDeleted)
|
||||
.AnyAsync();
|
||||
|
||||
if (hasModels)
|
||||
{
|
||||
throw new Volo.Abp.UserFriendlyException("该应用下存在模型,无法删除");
|
||||
}
|
||||
|
||||
await _appRepository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AI模型管理
|
||||
|
||||
/// <summary>
|
||||
/// 获取AI模型列表
|
||||
/// </summary>
|
||||
[HttpGet("channel/model")]
|
||||
public async Task<PagedResultDto<AiModelDto>> GetModelListAsync(AiModelGetListInput input)
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
|
||||
var query = _modelRepository._DbQueryable
|
||||
.Where(x => !x.IsDeleted)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x =>
|
||||
x.Name.Contains(input.SearchKey) || x.ModelId.Contains(input.SearchKey))
|
||||
.WhereIF(input.AiAppId.HasValue, x => x.AiAppId == input.AiAppId.Value)
|
||||
.WhereIF(input.IsPremiumOnly == true, x => x.IsPremium);
|
||||
|
||||
var entities = await query
|
||||
.OrderBy(x => x.OrderNum)
|
||||
.OrderByDescending(x => x.Id)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||
|
||||
var output = entities.Adapt<List<AiModelDto>>();
|
||||
return new PagedResultDto<AiModelDto>(total, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取AI模型
|
||||
/// </summary>
|
||||
[HttpGet("channel/model/{id}")]
|
||||
public async Task<AiModelDto> GetModelByIdAsync([FromRoute]Guid id)
|
||||
{
|
||||
var entity = await _modelRepository.GetByIdAsync(id);
|
||||
return entity.Adapt<AiModelDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建AI模型
|
||||
/// </summary>
|
||||
public async Task<AiModelDto> CreateModelAsync(AiModelCreateInput input)
|
||||
{
|
||||
// 验证应用是否存在
|
||||
var appExists = await _appRepository._DbQueryable
|
||||
.Where(x => x.Id == input.AiAppId)
|
||||
.AnyAsync();
|
||||
|
||||
if (!appExists)
|
||||
{
|
||||
throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在");
|
||||
}
|
||||
|
||||
var entity = new AiModelEntity
|
||||
{
|
||||
HandlerName = input.HandlerName,
|
||||
ModelId = input.ModelId,
|
||||
Name = input.Name,
|
||||
Description = input.Description,
|
||||
OrderNum = input.OrderNum,
|
||||
AiAppId = input.AiAppId,
|
||||
ExtraInfo = input.ExtraInfo,
|
||||
ModelType = input.ModelType,
|
||||
ModelApiType = input.ModelApiType,
|
||||
Multiplier = input.Multiplier,
|
||||
MultiplierShow = input.MultiplierShow,
|
||||
ProviderName = input.ProviderName,
|
||||
IconUrl = input.IconUrl,
|
||||
IsPremium = input.IsPremium,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
await _modelRepository.InsertAsync(entity);
|
||||
return entity.Adapt<AiModelDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新AI模型
|
||||
/// </summary>
|
||||
public async Task<AiModelDto> UpdateModelAsync(AiModelUpdateInput input)
|
||||
{
|
||||
var entity = await _modelRepository.GetByIdAsync(input.Id);
|
||||
|
||||
// 验证应用是否存在
|
||||
if (entity.AiAppId != input.AiAppId)
|
||||
{
|
||||
var appExists = await _appRepository._DbQueryable
|
||||
.Where(x => x.Id == input.AiAppId)
|
||||
.AnyAsync();
|
||||
|
||||
if (!appExists)
|
||||
{
|
||||
throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在");
|
||||
}
|
||||
}
|
||||
|
||||
entity.HandlerName = input.HandlerName;
|
||||
entity.ModelId = input.ModelId;
|
||||
entity.Name = input.Name;
|
||||
entity.Description = input.Description;
|
||||
entity.OrderNum = input.OrderNum;
|
||||
entity.AiAppId = input.AiAppId;
|
||||
entity.ExtraInfo = input.ExtraInfo;
|
||||
entity.ModelType = input.ModelType;
|
||||
entity.ModelApiType = input.ModelApiType;
|
||||
entity.Multiplier = input.Multiplier;
|
||||
entity.MultiplierShow = input.MultiplierShow;
|
||||
entity.ProviderName = input.ProviderName;
|
||||
entity.IconUrl = input.IconUrl;
|
||||
entity.IsPremium = input.IsPremium;
|
||||
|
||||
await _modelRepository.UpdateAsync(entity);
|
||||
return entity.Adapt<AiModelDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除AI模型(软删除)
|
||||
/// </summary>
|
||||
[HttpDelete("channel/model/{id}")]
|
||||
public async Task DeleteModelAsync(Guid id)
|
||||
{
|
||||
await _modelRepository.DeleteByIdAsync(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using Dm.util;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ModelContextProtocol;
|
||||
using ModelContextProtocol.Server;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using OpenAI.Chat;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
using Yi.Framework.AiHub.Domain;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||||
using Yi.Framework.AiHub.Domain.Extensions;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Dtos;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
using Yi.Framework.Rbac.Application.Contracts.IServices;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Dtos;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// ai服务
|
||||
/// </summary>
|
||||
public class AiChatService : ApplicationService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||
private readonly ILogger<AiChatService> _logger;
|
||||
private readonly AiGateWayManager _aiGateWayManager;
|
||||
private readonly ModelManager _modelManager;
|
||||
private readonly PremiumPackageManager _premiumPackageManager;
|
||||
private readonly ChatManager _chatManager;
|
||||
private readonly TokenManager _tokenManager;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly ISqlSugarRepository<AgentStoreAggregateRoot> _agentStoreRepository;
|
||||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||||
|
||||
public AiChatService(IHttpContextAccessor httpContextAccessor,
|
||||
AiBlacklistManager aiBlacklistManager,
|
||||
ILogger<AiChatService> logger,
|
||||
AiGateWayManager aiGateWayManager,
|
||||
ModelManager modelManager,
|
||||
PremiumPackageManager premiumPackageManager,
|
||||
ChatManager chatManager, TokenManager tokenManager, IAccountService accountService,
|
||||
ISqlSugarRepository<AgentStoreAggregateRoot> agentStoreRepository, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_aiBlacklistManager = aiBlacklistManager;
|
||||
_logger = logger;
|
||||
_aiGateWayManager = aiGateWayManager;
|
||||
_modelManager = modelManager;
|
||||
_premiumPackageManager = premiumPackageManager;
|
||||
_chatManager = chatManager;
|
||||
_tokenManager = tokenManager;
|
||||
_accountService = accountService;
|
||||
_agentStoreRepository = agentStoreRepository;
|
||||
_aiModelRepository = aiModelRepository;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 查询已登录的账户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Route("ai-chat/account")]
|
||||
[Authorize]
|
||||
public async Task<UserRoleMenuDto> GetAsync()
|
||||
{
|
||||
var accountService = LazyServiceProvider.GetRequiredService<IAccountService>();
|
||||
var output = await accountService.GetAsync();
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<ModelGetListOutput>> GetModelAsync()
|
||||
{
|
||||
var output = await _aiModelRepository._DbQueryable
|
||||
.Where(x => x.ModelType == ModelTypeEnum.Chat)
|
||||
.Where(x => x.ModelApiType == ModelApiTypeEnum.OpenAi)
|
||||
.OrderByDescending(x => x.OrderNum)
|
||||
.Select(x => new ModelGetListOutput
|
||||
{
|
||||
Id = x.Id,
|
||||
Category = "chat",
|
||||
ModelId = x.ModelId,
|
||||
ModelName = x.Name,
|
||||
ModelDescribe = x.Description,
|
||||
ModelPrice = 0,
|
||||
ModelType = "1",
|
||||
ModelShow = "0",
|
||||
SystemPrompt = null,
|
||||
ApiHost = null,
|
||||
ApiKey = null,
|
||||
Remark = x.Description,
|
||||
IsPremiumPackage = x.IsPremium
|
||||
}).ToListAsync();
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="sessionId"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
[HttpPost("ai-chat/send")]
|
||||
public async Task PostSendAsync([FromBody] ThorChatCompletionsRequest input, [FromQuery] Guid? sessionId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//除了免费模型,其他的模型都要校验
|
||||
if (!input.Model.Contains("DeepSeek-R1"))
|
||||
{
|
||||
//有token,需要黑名单校验
|
||||
if (CurrentUser.IsAuthenticated)
|
||||
{
|
||||
await _aiBlacklistManager.VerifiyAiBlacklist(CurrentUser.GetId());
|
||||
if (!CurrentUser.IsAiVip())
|
||||
{
|
||||
throw new UserFriendlyException("该模型需要VIP用户才能使用,请购买VIP后重新登录重试");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new UserFriendlyException("未登录用户,只能使用未加速的DeepSeek-R1,请登录后重试");
|
||||
}
|
||||
}
|
||||
|
||||
//如果是尊享包服务,需要校验是是否尊享包足够
|
||||
if (CurrentUser.IsAuthenticated)
|
||||
{
|
||||
var isPremium = await _modelManager.IsPremiumModelAsync(input.Model);
|
||||
|
||||
if (isPremium)
|
||||
{
|
||||
// 检查尊享token包用量
|
||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId());
|
||||
if (availableTokens <= 0)
|
||||
{
|
||||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ai网关代理httpcontext
|
||||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||
CurrentUser.Id, sessionId, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
[HttpPost("ai-chat/FileMaster/send")]
|
||||
public async Task PostFileMasterSendAsync([FromBody] ThorChatCompletionsRequest input,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(input.Model))
|
||||
{
|
||||
throw new BusinessException("当前接口不支持第三方使用");
|
||||
}
|
||||
|
||||
if (CurrentUser.IsAuthenticated)
|
||||
{
|
||||
await _aiBlacklistManager.VerifiyAiBlacklist(CurrentUser.GetId());
|
||||
if (CurrentUser.IsAiVip())
|
||||
{
|
||||
input.Model = "gpt-5-chat";
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Model = "gpt-4.1-mini";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Model = "DeepSeek-R1-0528";
|
||||
}
|
||||
|
||||
//ai网关代理httpcontext
|
||||
await _aiGateWayManager.CompleteChatStreamForStatisticsAsync(_httpContextAccessor.HttpContext, input,
|
||||
CurrentUser.Id, null, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Agent 发送消息
|
||||
/// </summary>
|
||||
[HttpPost("ai-chat/agent/send")]
|
||||
public async Task PostAgentSendAsync([FromBody] AgentSendInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
var tokenValidation = await _tokenManager.ValidateTokenAsync(input.Token, input.ModelId);
|
||||
|
||||
await _aiBlacklistManager.VerifiyAiBlacklist(tokenValidation.UserId);
|
||||
// 验证用户是否为VIP
|
||||
var userInfo = await _accountService.GetAsync(null, null, tokenValidation.UserId);
|
||||
if (userInfo == null)
|
||||
{
|
||||
throw new UserFriendlyException("用户信息不存在");
|
||||
}
|
||||
|
||||
// 检查是否为VIP(使用RoleCodes判断)
|
||||
if (!userInfo.RoleCodes.Contains(AiHubConst.VipRole) && userInfo.User.UserName != "cc")
|
||||
{
|
||||
throw new UserFriendlyException("该接口为尊享服务专用,需要VIP权限才能使用");
|
||||
}
|
||||
|
||||
//如果是尊享包服务,需要校验是是否尊享包足够
|
||||
var isPremium = await _modelManager.IsPremiumModelAsync(input.ModelId);
|
||||
|
||||
if (isPremium)
|
||||
{
|
||||
// 检查尊享token包用量
|
||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId);
|
||||
if (availableTokens <= 0)
|
||||
{
|
||||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||||
}
|
||||
}
|
||||
|
||||
await _chatManager.AgentCompleteChatStreamAsync(_httpContextAccessor.HttpContext,
|
||||
input.SessionId,
|
||||
input.Content,
|
||||
input.Token,
|
||||
tokenValidation.TokenId,
|
||||
input.ModelId,
|
||||
tokenValidation.UserId,
|
||||
input.Tools,
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Agent 工具
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("ai-chat/agent/tool")]
|
||||
public List<AgentToolOutput> GetAgentToolAsync()
|
||||
{
|
||||
var agentTools = _chatManager.GetTools().Select(x => new AgentToolOutput
|
||||
{
|
||||
Code = x.Code,
|
||||
Name = x.Name
|
||||
}).ToList();
|
||||
return agentTools;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Agent 上下文
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("ai-chat/agent/context/{sessionId}")]
|
||||
[Authorize]
|
||||
public async Task<string?> GetAgentContextAsync([FromRoute] Guid sessionId)
|
||||
{
|
||||
var data = await _agentStoreRepository.GetFirstAsync(x => x.SessionId == sessionId);
|
||||
return data?.Store;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.BackgroundJobs;
|
||||
using Volo.Abp.Guids;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat;
|
||||
using Yi.Framework.AiHub.Application.Jobs;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.AiHub.Domain.Extensions;
|
||||
using Yi.Framework.AiHub.Domain.Managers;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||||
using Yi.Framework.AiHub.Domain.Shared.Enums;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// AI图片生成服务
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public class AiImageService : ApplicationService
|
||||
{
|
||||
private readonly ISqlSugarRepository<ImageStoreTaskAggregateRoot> _imageTaskRepository;
|
||||
private readonly IBackgroundJobManager _backgroundJobManager;
|
||||
private readonly AiBlacklistManager _aiBlacklistManager;
|
||||
private readonly PremiumPackageManager _premiumPackageManager;
|
||||
private readonly ModelManager _modelManager;
|
||||
private readonly IGuidGenerator _guidGenerator;
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
|
||||
public AiImageService(
|
||||
ISqlSugarRepository<ImageStoreTaskAggregateRoot> imageTaskRepository,
|
||||
IBackgroundJobManager backgroundJobManager,
|
||||
AiBlacklistManager aiBlacklistManager,
|
||||
PremiumPackageManager premiumPackageManager,
|
||||
ModelManager modelManager,
|
||||
IGuidGenerator guidGenerator,
|
||||
IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
_imageTaskRepository = imageTaskRepository;
|
||||
_backgroundJobManager = backgroundJobManager;
|
||||
_aiBlacklistManager = aiBlacklistManager;
|
||||
_premiumPackageManager = premiumPackageManager;
|
||||
_modelManager = modelManager;
|
||||
_guidGenerator = guidGenerator;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成图片(异步任务)
|
||||
/// </summary>
|
||||
/// <param name="input">图片生成输入参数</param>
|
||||
/// <returns>任务ID</returns>
|
||||
[HttpPost("ai-image/generate")]
|
||||
[Authorize]
|
||||
public async Task<Guid> GenerateAsync([FromBody] ImageGenerationInput input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
|
||||
// 黑名单校验
|
||||
await _aiBlacklistManager.VerifiyAiBlacklist(userId);
|
||||
|
||||
// VIP校验
|
||||
if (!CurrentUser.IsAiVip())
|
||||
{
|
||||
throw new UserFriendlyException("图片生成功能需要VIP用户才能使用,请购买VIP后重新登录重试");
|
||||
}
|
||||
|
||||
// 尊享包校验 - 使用ModelManager统一判断
|
||||
var isPremium = await _modelManager.IsPremiumModelAsync(input.ModelId);
|
||||
if (isPremium)
|
||||
{
|
||||
var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId);
|
||||
if (availableTokens <= 0)
|
||||
{
|
||||
throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包");
|
||||
}
|
||||
}
|
||||
|
||||
// 创建任务实体
|
||||
var task = new ImageStoreTaskAggregateRoot
|
||||
{
|
||||
Prompt = input.Prompt,
|
||||
ReferenceImagesBase64 = input.ReferenceImagesBase64 ?? new List<string>(),
|
||||
ReferenceImagesUrl = new List<string>(),
|
||||
TaskStatus = TaskStatusEnum.Processing,
|
||||
UserId = userId
|
||||
};
|
||||
|
||||
await _imageTaskRepository.InsertAsync(task);
|
||||
var taskId = task.Id;
|
||||
|
||||
// 构建请求JSON
|
||||
var requestJson = JsonSerializer.Serialize(new
|
||||
{
|
||||
prompt = input.Prompt,
|
||||
referenceImages = input.ReferenceImagesBase64
|
||||
});
|
||||
|
||||
// 入队后台任务
|
||||
await _backgroundJobManager.EnqueueAsync(new ImageGenerationJobArgs
|
||||
{
|
||||
TaskId = taskId,
|
||||
ModelId = input.ModelId,
|
||||
RequestJson = requestJson,
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
return taskId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询任务状态
|
||||
/// </summary>
|
||||
/// <param name="taskId">任务ID</param>
|
||||
/// <returns>任务详情</returns>
|
||||
[HttpGet("ai-image/task/{taskId}")]
|
||||
public async Task<ImageTaskOutput> GetTaskAsync([FromRoute] Guid taskId)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
|
||||
var task = await _imageTaskRepository.GetFirstAsync(x => x.Id == taskId && x.UserId == userId);
|
||||
if (task == null)
|
||||
{
|
||||
throw new UserFriendlyException("任务不存在或无权访问");
|
||||
}
|
||||
|
||||
return new ImageTaskOutput
|
||||
{
|
||||
Id = task.Id,
|
||||
Prompt = task.Prompt,
|
||||
ReferenceImagesBase64 = task.ReferenceImagesBase64,
|
||||
ReferenceImagesUrl = task.ReferenceImagesUrl,
|
||||
StoreBase64 = task.StoreBase64,
|
||||
StoreUrl = task.StoreUrl,
|
||||
TaskStatus = task.TaskStatus,
|
||||
CreationTime = task.CreationTime
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传Base64图片转换为URL
|
||||
/// </summary>
|
||||
/// <param name="base64Data">Base64图片数据(包含前缀如 data:image/png;base64,)</param>
|
||||
/// <returns>图片访问URL</returns>
|
||||
[HttpPost("ai-image/upload-base64")]
|
||||
public async Task<string> UploadBase64ToUrlAsync([FromBody] string base64Data)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(base64Data))
|
||||
{
|
||||
throw new UserFriendlyException("Base64数据不能为空");
|
||||
}
|
||||
|
||||
// 解析Base64数据
|
||||
string mimeType = "image/png";
|
||||
string base64Content = base64Data;
|
||||
|
||||
if (base64Data.Contains(","))
|
||||
{
|
||||
var parts = base64Data.Split(',');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
// 提取MIME类型
|
||||
var header = parts[0];
|
||||
if (header.Contains(":") && header.Contains(";"))
|
||||
{
|
||||
mimeType = header.Split(':')[1].Split(';')[0];
|
||||
}
|
||||
base64Content = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件扩展名
|
||||
var extension = mimeType switch
|
||||
{
|
||||
"image/png" => ".png",
|
||||
"image/jpeg" => ".jpg",
|
||||
"image/jpg" => ".jpg",
|
||||
"image/gif" => ".gif",
|
||||
"image/webp" => ".webp",
|
||||
_ => ".png"
|
||||
};
|
||||
|
||||
// 解码Base64
|
||||
byte[] imageBytes;
|
||||
try
|
||||
{
|
||||
imageBytes = Convert.FromBase64String(base64Content);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
throw new UserFriendlyException("Base64格式无效");
|
||||
}
|
||||
|
||||
// 创建存储目录
|
||||
var uploadPath = Path.Combine(_webHostEnvironment.ContentRootPath, "wwwroot", "ai-images");
|
||||
if (!Directory.Exists(uploadPath))
|
||||
{
|
||||
Directory.CreateDirectory(uploadPath);
|
||||
}
|
||||
|
||||
// 生成文件名并保存
|
||||
var fileId = _guidGenerator.Create();
|
||||
var fileName = $"{fileId}{extension}";
|
||||
var filePath = Path.Combine(uploadPath, fileName);
|
||||
|
||||
await File.WriteAllBytesAsync(filePath, imageBytes);
|
||||
|
||||
// 返回访问URL
|
||||
return $"/ai-images/{fileName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询任务列表
|
||||
/// </summary>
|
||||
/// <param name="input">分页查询参数</param>
|
||||
/// <returns>任务列表</returns>
|
||||
[HttpGet("ai-image/tasks")]
|
||||
public async Task<PagedResult<ImageTaskOutput>> GetTaskPageAsync([FromQuery] ImageTaskPageInput input)
|
||||
{
|
||||
var userId = CurrentUser.GetId();
|
||||
|
||||
var query = _imageTaskRepository._DbQueryable
|
||||
.Where(x => x.UserId == userId)
|
||||
.WhereIF(input.TaskStatus.HasValue, x => x.TaskStatus == input.TaskStatus!.Value)
|
||||
.OrderByDescending(x => x.CreationTime);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query
|
||||
.Skip((input.PageIndex - 1) * input.PageSize)
|
||||
.Take(input.PageSize)
|
||||
.Select(x => new ImageTaskOutput
|
||||
{
|
||||
Id = x.Id,
|
||||
Prompt = x.Prompt,
|
||||
ReferenceImagesBase64 = x.ReferenceImagesBase64,
|
||||
ReferenceImagesUrl = x.ReferenceImagesUrl,
|
||||
StoreBase64 = x.StoreBase64,
|
||||
StoreUrl = x.StoreUrl,
|
||||
TaskStatus = x.TaskStatus,
|
||||
CreationTime = x.CreationTime
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return new PagedResult<ImageTaskOutput>(total, items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分页结果
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型</typeparam>
|
||||
public class PagedResult<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 总数
|
||||
/// </summary>
|
||||
public long Total { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据列表
|
||||
/// </summary>
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
public PagedResult(long total, List<T> items)
|
||||
{
|
||||
Total = total;
|
||||
Items = items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SqlSugar;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Users;
|
||||
using Yi.Framework.AiHub.Application.Contracts.Dtos;
|
||||
using Yi.Framework.AiHub.Domain.Entities;
|
||||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
namespace Yi.Framework.AiHub.Application.Services;
|
||||
|
||||
public class MessageService : ApplicationService
|
||||
{
|
||||
private readonly ISqlSugarRepository<MessageAggregateRoot> _repository;
|
||||
|
||||
public MessageService(ISqlSugarRepository<MessageAggregateRoot> repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询消息
|
||||
/// 需要会话id
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
public async Task<PagedResultDto<MessageDto>> GetListAsync([FromQuery]MessageGetListInput input)
|
||||
{
|
||||
RefAsync<int> total = 0;
|
||||
var userId = CurrentUser.GetId();
|
||||
var entities = await _repository._DbQueryable
|
||||
.Where(x => x.SessionId == input.SessionId)
|
||||
.Where(x=>x.UserId == userId)
|
||||
.OrderBy(x => x.Id)
|
||||
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
|
||||
return new PagedResultDto<MessageDto>(total, entities.Adapt<List<MessageDto>>());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user