ai-engineering-init 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/.claude/agents/code-reviewer.md +3 -130
  2. package/.claude/hooks/skill-forced-eval.js +46 -60
  3. package/.claude/hooks/stop.js +24 -1
  4. package/.claude/settings.json +10 -1
  5. package/.claude/skills/api-development/SKILL.md +179 -130
  6. package/.claude/skills/architecture-design/SKILL.md +102 -212
  7. package/.claude/skills/backend-annotations/SKILL.md +166 -220
  8. package/.claude/skills/bug-detective/SKILL.md +225 -186
  9. package/.claude/skills/code-patterns/SKILL.md +127 -244
  10. package/.claude/skills/codex-code-review/SKILL.md +327 -0
  11. package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
  12. package/.claude/skills/crud-development/SKILL.md +226 -307
  13. package/.claude/skills/data-permission/SKILL.md +131 -202
  14. package/.claude/skills/database-ops/SKILL.md +158 -355
  15. package/.claude/skills/error-handler/SKILL.md +224 -285
  16. package/.claude/skills/file-oss-management/SKILL.md +174 -169
  17. package/.claude/skills/git-workflow/SKILL.md +123 -341
  18. package/.claude/skills/json-serialization/SKILL.md +121 -137
  19. package/.claude/skills/leniu-report-customization/SKILL.md +82 -2
  20. package/.claude/skills/leniu-report-standard-customization/SKILL.md +65 -2
  21. package/.claude/skills/loki-log-query/SKILL.md +400 -0
  22. package/.claude/skills/mysql-debug/SKILL.md +58 -22
  23. package/.claude/skills/performance-doctor/SKILL.md +83 -89
  24. package/.claude/skills/redis-cache/SKILL.md +134 -185
  25. package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
  26. package/.claude/skills/security-guard/SKILL.md +168 -276
  27. package/.claude/skills/sms-mail/SKILL.md +266 -228
  28. package/.claude/skills/social-login/SKILL.md +257 -195
  29. package/.claude/skills/sync-back-merge/SKILL.md +66 -0
  30. package/.claude/skills/tenant-management/SKILL.md +172 -188
  31. package/.claude/skills/utils-toolkit/SKILL.md +214 -222
  32. package/.claude/skills/websocket-sse/SKILL.md +251 -172
  33. package/.claude/skills/workflow-engine/SKILL.md +178 -250
  34. package/.claude/skills/yunxiao-task-management/SKILL.md +489 -0
  35. package/.codex/skills/api-development/SKILL.md +179 -130
  36. package/.codex/skills/architecture-design/SKILL.md +102 -212
  37. package/.codex/skills/backend-annotations/SKILL.md +166 -220
  38. package/.codex/skills/bug-detective/SKILL.md +225 -186
  39. package/.codex/skills/code-patterns/SKILL.md +127 -244
  40. package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
  41. package/.codex/skills/crud-development/SKILL.md +226 -307
  42. package/.codex/skills/data-permission/SKILL.md +131 -202
  43. package/.codex/skills/database-ops/SKILL.md +158 -355
  44. package/.codex/skills/error-handler/SKILL.md +224 -285
  45. package/.codex/skills/file-oss-management/SKILL.md +174 -169
  46. package/.codex/skills/git-workflow/SKILL.md +123 -341
  47. package/.codex/skills/json-serialization/SKILL.md +121 -137
  48. package/.codex/skills/leniu-report-customization/SKILL.md +82 -2
  49. package/.codex/skills/leniu-report-standard-customization/SKILL.md +65 -2
  50. package/.codex/skills/loki-log-query/SKILL.md +400 -0
  51. package/.codex/skills/loki-log-query/environments.json +45 -0
  52. package/.codex/skills/mysql-debug/SKILL.md +58 -22
  53. package/.codex/skills/performance-doctor/SKILL.md +83 -89
  54. package/.codex/skills/redis-cache/SKILL.md +134 -185
  55. package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
  56. package/.codex/skills/security-guard/SKILL.md +168 -276
  57. package/.codex/skills/skill-creator/LICENSE.txt +202 -0
  58. package/.codex/skills/skill-creator/SKILL.md +479 -0
  59. package/.codex/skills/skill-creator/agents/analyzer.md +274 -0
  60. package/.codex/skills/skill-creator/agents/comparator.md +202 -0
  61. package/.codex/skills/skill-creator/agents/grader.md +223 -0
  62. package/.codex/skills/skill-creator/assets/eval_review.html +146 -0
  63. package/.codex/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  64. package/.codex/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  65. package/.codex/skills/skill-creator/references/schemas.md +430 -0
  66. package/.codex/skills/skill-creator/scripts/__init__.py +0 -0
  67. package/.codex/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  68. package/.codex/skills/skill-creator/scripts/generate_report.py +326 -0
  69. package/.codex/skills/skill-creator/scripts/improve_description.py +248 -0
  70. package/.codex/skills/skill-creator/scripts/package_skill.py +136 -0
  71. package/.codex/skills/skill-creator/scripts/quick_validate.py +103 -0
  72. package/.codex/skills/skill-creator/scripts/run_eval.py +310 -0
  73. package/.codex/skills/skill-creator/scripts/run_loop.py +332 -0
  74. package/.codex/skills/skill-creator/scripts/utils.py +47 -0
  75. package/.codex/skills/sms-mail/SKILL.md +266 -228
  76. package/.codex/skills/social-login/SKILL.md +257 -195
  77. package/.codex/skills/sync-back-merge/SKILL.md +66 -0
  78. package/.codex/skills/tenant-management/SKILL.md +172 -188
  79. package/.codex/skills/utils-toolkit/SKILL.md +214 -222
  80. package/.codex/skills/websocket-sse/SKILL.md +251 -172
  81. package/.codex/skills/workflow-engine/SKILL.md +178 -250
  82. package/.codex/skills/yunxiao-task-management/SKILL.md +489 -0
  83. package/.cursor/hooks/cursor-skill-eval.js +66 -6
  84. package/.cursor/hooks/stop.js +23 -1
  85. package/.cursor/skills/api-development/SKILL.md +179 -130
  86. package/.cursor/skills/architecture-design/SKILL.md +102 -212
  87. package/.cursor/skills/backend-annotations/SKILL.md +166 -220
  88. package/.cursor/skills/bug-detective/SKILL.md +225 -186
  89. package/.cursor/skills/code-patterns/SKILL.md +127 -244
  90. package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
  91. package/.cursor/skills/crud-development/SKILL.md +226 -307
  92. package/.cursor/skills/data-permission/SKILL.md +131 -202
  93. package/.cursor/skills/database-ops/SKILL.md +158 -355
  94. package/.cursor/skills/error-handler/SKILL.md +224 -285
  95. package/.cursor/skills/file-oss-management/SKILL.md +174 -169
  96. package/.cursor/skills/git-workflow/SKILL.md +123 -341
  97. package/.cursor/skills/json-serialization/SKILL.md +121 -137
  98. package/.cursor/skills/leniu-report-customization/SKILL.md +82 -2
  99. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +65 -2
  100. package/.cursor/skills/loki-log-query/SKILL.md +400 -0
  101. package/.cursor/skills/loki-log-query/environments.json +45 -0
  102. package/.cursor/skills/mysql-debug/SKILL.md +58 -22
  103. package/.cursor/skills/performance-doctor/SKILL.md +83 -89
  104. package/.cursor/skills/redis-cache/SKILL.md +134 -185
  105. package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
  106. package/.cursor/skills/security-guard/SKILL.md +168 -276
  107. package/.cursor/skills/skill-creator/LICENSE.txt +202 -0
  108. package/.cursor/skills/skill-creator/SKILL.md +479 -0
  109. package/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
  110. package/.cursor/skills/skill-creator/agents/comparator.md +202 -0
  111. package/.cursor/skills/skill-creator/agents/grader.md +223 -0
  112. package/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
  113. package/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  114. package/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  115. package/.cursor/skills/skill-creator/references/schemas.md +430 -0
  116. package/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
  117. package/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  118. package/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
  119. package/.cursor/skills/skill-creator/scripts/improve_description.py +248 -0
  120. package/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
  121. package/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
  122. package/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
  123. package/.cursor/skills/skill-creator/scripts/run_loop.py +332 -0
  124. package/.cursor/skills/skill-creator/scripts/utils.py +47 -0
  125. package/.cursor/skills/sms-mail/SKILL.md +266 -228
  126. package/.cursor/skills/social-login/SKILL.md +257 -195
  127. package/.cursor/skills/sync-back-merge/SKILL.md +66 -0
  128. package/.cursor/skills/tenant-management/SKILL.md +172 -188
  129. package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
  130. package/.cursor/skills/websocket-sse/SKILL.md +251 -172
  131. package/.cursor/skills/workflow-engine/SKILL.md +178 -250
  132. package/.cursor/skills/yunxiao-task-management/SKILL.md +489 -0
  133. package/AGENTS.md +49 -540
  134. package/CLAUDE.md +73 -119
  135. package/README.md +37 -6
  136. package/bin/index.js +611 -25
  137. package/package.json +1 -1
  138. package/src/platform-map.json +4 -0
  139. package/src/skills/api-development/SKILL.md +179 -130
  140. package/src/skills/architecture-design/SKILL.md +102 -212
  141. package/src/skills/backend-annotations/SKILL.md +166 -220
  142. package/src/skills/bug-detective/SKILL.md +225 -186
  143. package/src/skills/code-patterns/SKILL.md +127 -244
  144. package/src/skills/codex-code-review/SKILL.md +261 -69
  145. package/src/skills/collaborating-with-codex/SKILL.md +96 -113
  146. package/src/skills/crud-development/SKILL.md +226 -307
  147. package/src/skills/data-permission/SKILL.md +131 -202
  148. package/src/skills/database-ops/SKILL.md +158 -355
  149. package/src/skills/error-handler/SKILL.md +224 -285
  150. package/src/skills/file-oss-management/SKILL.md +174 -169
  151. package/src/skills/git-workflow/SKILL.md +123 -341
  152. package/src/skills/json-serialization/SKILL.md +121 -137
  153. package/src/skills/leniu-report-customization/SKILL.md +82 -2
  154. package/src/skills/leniu-report-standard-customization/SKILL.md +65 -2
  155. package/src/skills/loki-log-query/SKILL.md +400 -0
  156. package/src/skills/loki-log-query/environments.json +45 -0
  157. package/src/skills/mysql-debug/SKILL.md +58 -22
  158. package/src/skills/performance-doctor/SKILL.md +83 -89
  159. package/src/skills/redis-cache/SKILL.md +134 -185
  160. package/src/skills/scheduled-jobs/SKILL.md +187 -224
  161. package/src/skills/security-guard/SKILL.md +168 -276
  162. package/src/skills/skill-creator/LICENSE.txt +202 -0
  163. package/src/skills/skill-creator/SKILL.md +479 -0
  164. package/src/skills/skill-creator/agents/analyzer.md +274 -0
  165. package/src/skills/skill-creator/agents/comparator.md +202 -0
  166. package/src/skills/skill-creator/agents/grader.md +223 -0
  167. package/src/skills/skill-creator/assets/eval_review.html +146 -0
  168. package/src/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  169. package/src/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  170. package/src/skills/skill-creator/references/schemas.md +430 -0
  171. package/src/skills/skill-creator/scripts/__init__.py +0 -0
  172. package/src/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  173. package/src/skills/skill-creator/scripts/generate_report.py +326 -0
  174. package/src/skills/skill-creator/scripts/improve_description.py +248 -0
  175. package/src/skills/skill-creator/scripts/package_skill.py +136 -0
  176. package/src/skills/skill-creator/scripts/quick_validate.py +103 -0
  177. package/src/skills/skill-creator/scripts/run_eval.py +310 -0
  178. package/src/skills/skill-creator/scripts/run_loop.py +332 -0
  179. package/src/skills/skill-creator/scripts/utils.py +47 -0
  180. package/src/skills/sms-mail/SKILL.md +266 -228
  181. package/src/skills/social-login/SKILL.md +257 -195
  182. package/src/skills/sync-back-merge/SKILL.md +66 -0
  183. package/src/skills/tenant-management/SKILL.md +172 -188
  184. package/src/skills/utils-toolkit/SKILL.md +214 -222
  185. package/src/skills/websocket-sse/SKILL.md +251 -172
  186. package/src/skills/workflow-engine/SKILL.md +178 -250
  187. package/src/skills/yunxiao-task-management/SKILL.md +489 -0
@@ -1,266 +1,328 @@
1
1
  ---
2
2
  name: social-login
3
3
  description: |
4
- 当需要实现第三方登录、OAuth2 认证、社交账号绑定时自动使用此 Skill。
5
-
4
+ 通用 OAuth2 第三方登录开发指南。涵盖授权码流程、接口设计、账号绑定机制、多平台接入。
6
5
  触发场景:
7
- - 需要接入微信/QQ/支付宝等第三方登录
8
- - 需要实现 OAuth2 授权流程
9
- - 需要配置 JustAuth 第三方登录
10
- - 需要实现社交账号与系统账号绑定
11
- - 需要获取第三方用户信息
12
-
13
- 触发词:第三方登录、微信登录、QQ登录、OAuth、OAuth2、JustAuth、社交登录、扫码登录、AuthRequest、SocialUtils、授权登录、GitHub登录、钉钉登录
6
+ - 接入微信/QQ/GitHub 等第三方登录
7
+ - 实现 OAuth2 授权码流程
8
+ - 实现社交账号与系统账号绑定/解绑
9
+ - 获取第三方用户信息
10
+ 触发词:第三方登录、OAuth、OAuth2、社交登录、微信登录、QQ登录、GitHub登录、扫码登录、授权码、授权登录
11
+ 注意:如果项目有专属技能,优先使用专属版本。
14
12
  ---
15
13
 
16
- # 第三方登录开发指南(JustAuth)
14
+ # OAuth2 第三方登录开发指南
17
15
 
18
- > **适用模块**:`ruoyi-common-social`(基于 JustAuth)
19
- > **特性**:Sa-Token 认证集成、Redis 状态缓存(防 CSRF)、多租户支持、账号绑定机制
16
+ > 通用模板。如果项目有专属技能,优先使用。
20
17
 
21
- ## 一、支持平台
18
+ ## 设计原则
22
19
 
23
- | 平台 | source 标识 | 平台 | source 标识 |
24
- |------|------------|------|------------|
25
- | 钉钉 | `dingtalk` | GitHub | `github` |
26
- | Gitee | `gitee` | 微博 | `weibo` |
27
- | 支付宝 | `alipay_wallet` | QQ | `qq` |
28
- | 微信开放平台 | `wechat_open` | 微信公众号 | `wechat_mp` |
29
- | 企业微信 | `wechat_enterprise` | 抖音 | `douyin` |
30
- | 华为 | `huawei` | 微软 | `microsoft` |
31
- | MaxKey | `maxkey` | TopIAM | `topiam` |
32
- | GitLab | `gitlab` | Gitea | `gitea` |
33
-
34
- 完整平台列表及特殊配置详见 `references/provider-configs.md`。
20
+ 1. **标准协议**:遵循 OAuth 2.0 授权码模式(Authorization Code Flow),这是最安全的 OAuth 流程。
21
+ 2. **状态防护**:使用 `state` 参数防止 CSRF 攻击,每次授权请求生成唯一 state 并校验。
22
+ 3. **绑定机制**:第三方账号与系统账号通过绑定表关联,支持一个系统账号绑定多个第三方平台。
23
+ 4. **信息最小化**:只获取必要的第三方用户信息(OpenID、昵称、头像),不过度获取。
35
24
 
36
25
  ---
37
26
 
38
- ## 二、基础配置
27
+ ## OAuth 2.0 授权码流程
39
28
 
40
- ```yaml
41
- justauth:
42
- address: https://your-domain.com # 回调地址前缀
43
- type:
44
- github:
45
- client-id: ${GITHUB_CLIENT_ID:}
46
- client-secret: ${GITHUB_CLIENT_SECRET:}
47
- redirect-uri: ${justauth.address}/social-callback?source=github
48
- gitee:
49
- client-id: ${GITEE_CLIENT_ID:}
50
- client-secret: ${GITEE_CLIENT_SECRET:}
51
- redirect-uri: ${justauth.address}/social-callback?source=gitee
52
- dingtalk:
53
- client-id: ${DINGTALK_APP_KEY:}
54
- client-secret: ${DINGTALK_APP_SECRET:}
55
- redirect-uri: ${justauth.address}/social-callback?source=dingtalk
29
+ ```
30
+ 用户 -> 前端 -> 后端(生成授权URL) -> 第三方平台(授权页)
31
+ |
32
+ 用户授权 |
33
+ v
34
+ 第三方平台 -> 前端回调页(携带 code + state)-> 后端
35
+ |
36
+ 后端用 code 换 access_token |
37
+ 后端用 access_token 获取用户信息 |
38
+ v
39
+ 查绑定关系 -> 登录/绑定
56
40
  ```
57
41
 
58
- > 各平台特殊配置(微软 tenantId、企业微信 agentId、支付宝公钥等)详见 `references/provider-configs.md`。
42
+ ### 步骤详解
59
43
 
60
- ---
44
+ | 步骤 | 描述 | 关键参数 |
45
+ |------|------|---------|
46
+ | 1. 构建授权 URL | 拼接第三方授权地址 | client_id, redirect_uri, state, scope |
47
+ | 2. 用户授权 | 用户在第三方平台确认授权 | - |
48
+ | 3. 回调获取 code | 第三方重定向回应用 | code, state |
49
+ | 4. code 换 token | 后端调用第三方 Token 接口 | code, client_id, client_secret |
50
+ | 5. 获取用户信息 | 后端调用第三方用户信息接口 | access_token |
51
+ | 6. 登录/绑定 | 根据 OpenID 查找绑定关系 | openId, source |
61
52
 
62
- ## 三、核心 API
53
+ ---
63
54
 
64
- ### 3.1 SocialUtils
55
+ ## 实现模式
65
56
 
66
- **位置**:`org.dromara.common.social.utils.SocialUtils`
57
+ ### 一、抽象接口设计
67
58
 
68
59
  ```java
69
- import org.dromara.common.social.utils.SocialUtils;
70
- import me.zhyd.oauth.model.AuthResponse;
71
- import me.zhyd.oauth.model.AuthUser;
72
- import me.zhyd.oauth.request.AuthRequest;
73
-
74
- // 获取授权请求对象
75
- AuthRequest authRequest = SocialUtils.getAuthRequest("github", socialProperties);
76
-
77
- // 生成授权 URL
78
- String authorizeUrl = authRequest.authorize(state);
60
+ // 第三方认证请求接口
61
+ public interface SocialAuthProvider {
62
+ String getSource(); // 平台标识
63
+ String buildAuthorizeUrl(String state); // 构建授权URL
64
+ SocialUser authenticate(String code, String state); // 回调认证
65
+ }
79
66
 
80
- // 处理回调登录
81
- AuthResponse<AuthUser> response = SocialUtils.loginAuth(
82
- "github", code, state, socialProperties
83
- );
84
- if (response.ok()) {
85
- AuthUser user = response.getData();
86
- String openId = user.getUuid(); // 唯一标识
87
- String nickname = user.getNickname(); // 昵称
88
- String source = user.getSource(); // 来源平台
67
+ // 第三方用户信息
68
+ @Data
69
+ public class SocialUser {
70
+ private String openId; // 平台唯一标识
71
+ private String source; // 来源平台(github, wechat_open 等)
72
+ private String nickname; // 昵称
73
+ private String avatar; // 头像
74
+ private String email; // 邮箱(可能为空)
75
+ private String accessToken; // 第三方 Token
76
+ private Map<String, Object> rawInfo; // 原始数据
89
77
  }
90
78
  ```
91
79
 
92
- ### 3.2 AuthUser 关键字段
80
+ ### 二、GitHub 实现示例
93
81
 
94
- | 属性 | 说明 | 属性 | 说明 |
95
- |------|------|------|------|
96
- | `uuid` | 平台用户唯一ID | `username` | 用户名 |
97
- | `nickname` | 昵称 | `avatar` | 头像 |
98
- | `email` | 邮箱 | `source` | 来源平台 |
99
- | `token` | Token 信息 | `rawUserInfo` | 原始数据(Map) |
82
+ ```java
83
+ @Component
84
+ public class GitHubAuthProvider implements SocialAuthProvider {
100
85
 
101
- ### 3.3 状态缓存
86
+ @Value("${social.github.client-id}")
87
+ private String clientId;
102
88
 
103
- `AuthRedisStateCache` 自动管理 OAuth2 state 参数(Redis 缓存,3分钟过期),无需手动操作。
89
+ @Value("${social.github.client-secret}")
90
+ private String clientSecret;
104
91
 
105
- ---
92
+ @Value("${social.github.redirect-uri}")
93
+ private String redirectUri;
106
94
 
107
- ## 四、后端实现
95
+ @Override
96
+ public String getSource() { return "github"; }
108
97
 
109
- ### 4.1 生成授权 URL
98
+ @Override
99
+ public String buildAuthorizeUrl(String state) {
100
+ return "https://github.com/login/oauth/authorize"
101
+ + "?client_id=" + clientId
102
+ + "&redirect_uri=" + URLEncoder.encode(redirectUri, StandardCharsets.UTF_8)
103
+ + "&state=" + state
104
+ + "&scope=user:email";
105
+ }
110
106
 
111
- ```java
112
- @GetMapping("/binding/{source}")
113
- public R<String> authBinding(@PathVariable String source) {
114
- AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
115
- String state = AuthStateUtils.createState();
116
- return R.ok("操作成功", authRequest.authorize(state));
107
+ @Override
108
+ public SocialUser authenticate(String code, String state) {
109
+ // 1. code access_token
110
+ String tokenUrl = "https://github.com/login/oauth/access_token";
111
+ Map<String, String> body = Map.of(
112
+ "client_id", clientId,
113
+ "client_secret", clientSecret,
114
+ "code", code
115
+ );
116
+ String accessToken = httpPost(tokenUrl, body); // 解析响应获取 token
117
+
118
+ // 2. 获取用户信息
119
+ String userInfo = httpGet("https://api.github.com/user",
120
+ Map.of("Authorization", "Bearer " + accessToken));
121
+
122
+ // 3. 构建 SocialUser
123
+ SocialUser user = new SocialUser();
124
+ user.setOpenId(parseField(userInfo, "id"));
125
+ user.setSource("github");
126
+ user.setNickname(parseField(userInfo, "login"));
127
+ user.setAvatar(parseField(userInfo, "avatar_url"));
128
+ user.setEmail(parseField(userInfo, "email"));
129
+ return user;
130
+ }
117
131
  }
118
132
  ```
119
133
 
120
- ### 4.2 回调登录(SocialAuthStrategy)
134
+ ### 三、Controller
121
135
 
122
136
  ```java
123
- @Slf4j
124
- @Service("social" + IAuthStrategy.BASE_NAME)
125
- @RequiredArgsConstructor
126
- public class SocialAuthStrategy implements IAuthStrategy {
137
+ @RestController
138
+ @RequestMapping("/auth/social")
139
+ public class SocialLoginController {
140
+
141
+ @Autowired
142
+ private Map<String, SocialAuthProvider> providers; // Spring 自动注入所有实现
143
+
144
+ @Autowired
145
+ private StringRedisTemplate redisTemplate;
146
+
147
+ @Autowired
148
+ private SocialBindService bindService;
149
+
150
+ // 1. 获取授权 URL
151
+ @GetMapping("/authorize/{source}")
152
+ public Result<String> authorize(@PathVariable String source) {
153
+ SocialAuthProvider provider = getProvider(source);
154
+ String state = UUID.randomUUID().toString().replace("-", "");
155
+ // state 存入 Redis,3分钟有效
156
+ redisTemplate.opsForValue().set("social:state:" + state, source, 3, TimeUnit.MINUTES);
157
+ return Result.ok(provider.buildAuthorizeUrl(state));
158
+ }
127
159
 
128
- @Override
129
- public LoginVo login(String body, SysClientVo client) {
130
- SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
131
- ValidatorUtils.validate(loginBody);
132
-
133
- // 1. 获取第三方用户信息
134
- AuthResponse<AuthUser> response = SocialUtils.loginAuth(
135
- loginBody.getSource(), loginBody.getSocialCode(),
136
- loginBody.getSocialState(), socialProperties);
137
- if (!response.ok()) {
138
- throw new ServiceException(response.getMsg());
160
+ // 2. 回调登录
161
+ @PostMapping("/callback")
162
+ public Result<?> callback(@RequestBody SocialCallbackDTO dto) {
163
+ // 校验 state
164
+ String cachedSource = redisTemplate.opsForValue().get("social:state:" + dto.getState());
165
+ if (cachedSource == null) {
166
+ throw new [你的异常类]("授权已过期,请重新操作");
139
167
  }
168
+ redisTemplate.delete("social:state:" + dto.getState());
169
+
170
+ // 获取第三方用户信息
171
+ SocialAuthProvider provider = getProvider(dto.getSource());
172
+ SocialUser socialUser = provider.authenticate(dto.getCode(), dto.getState());
140
173
 
141
- // 2. 查找绑定关系
142
- String authId = response.getData().getSource() + response.getData().getUuid();
143
- List<SysSocialVo> list = sysSocialService.selectByAuthId(authId);
144
- if (CollUtil.isEmpty(list)) {
145
- throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
174
+ // 查找绑定关系
175
+ String authId = socialUser.getSource() + ":" + socialUser.getOpenId();
176
+ SocialBind bind = bindService.findByAuthId(authId);
177
+
178
+ if (bind == null) {
179
+ // 未绑定 -> 返回第三方信息,引导绑定或注册
180
+ return Result.fail("NEED_BINDIND", "请绑定系统账号", socialUser);
146
181
  }
147
182
 
148
- // 3. 生成系统 Token
149
- LoginUser loginUser = loginService.buildLoginUser(loadUser(list.get(0).getUserId()));
150
- LoginHelper.login(loginUser, new SaLoginParameter()
151
- .setDeviceType(client.getDeviceType())
152
- .setTimeout(client.getTimeout())
153
- .setActiveTimeout(client.getActiveTimeout()));
154
-
155
- LoginVo loginVo = new LoginVo();
156
- loginVo.setAccessToken(StpUtil.getTokenValue());
157
- loginVo.setExpireIn(StpUtil.getTokenTimeout());
158
- return loginVo;
183
+ // 已绑定 -> 执行登录
184
+ LoginUser loginUser = loadUserById(bind.getUserId());
185
+ String token = [你的认证工具类].login(loginUser);
186
+ return Result.ok(Map.of("token", token));
159
187
  }
160
- }
161
- ```
162
188
 
163
- ### 4.3 账号绑定
189
+ // 3. 绑定(已登录用户绑定第三方账号)
190
+ @PostMapping("/bind")
191
+ public Result<?> bind(@RequestBody SocialCallbackDTO dto) {
192
+ [你的认证工具类].checkLogin();
193
+ Long currentUserId = [你的认证工具类].getCurrentUserId();
164
194
 
165
- ```java
166
- // 绑定:AuthController.socialCallback() → SysLoginService.socialRegister()
167
- @PostMapping("/social/callback")
168
- public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
169
- StpUtil.checkLogin();
170
- AuthResponse<AuthUser> response = SocialUtils.loginAuth(...);
171
- if (!response.ok()) return R.fail(response.getMsg());
172
- loginService.socialRegister(response.getData());
173
- return R.ok();
174
- }
195
+ SocialAuthProvider provider = getProvider(dto.getSource());
196
+ SocialUser socialUser = provider.authenticate(dto.getCode(), dto.getState());
175
197
 
176
- // socialRegister 核心逻辑:
177
- // 1. 生成 authId = source + uuid
178
- // 2. 检查 authId 是否已被其他用户绑定
179
- // 3. 查询当前用户是否已绑定该平台 -> 新增或更新
180
- ```
198
+ String authId = socialUser.getSource() + ":" + socialUser.getOpenId();
199
+ bindService.bindOrUpdate(currentUserId, authId, socialUser);
200
+ return Result.ok("绑定成功");
201
+ }
181
202
 
182
- ### 4.4 解绑
203
+ // 4. 解绑
204
+ @DeleteMapping("/unbind/{bindId}")
205
+ public Result<?> unbind(@PathVariable Long bindId) {
206
+ [你的认证工具类].checkLogin();
207
+ bindService.unbind(bindId, [你的认证工具类].getCurrentUserId());
208
+ return Result.ok("已解除绑定");
209
+ }
183
210
 
184
- ```java
185
- @DeleteMapping("/unlock/{socialId}")
186
- public R<Void> unlockSocial(@PathVariable Long socialId) {
187
- StpUtil.checkLogin();
188
- return socialUserService.deleteWithValidById(socialId) ? R.ok() : R.fail("取消授权失败");
211
+ private SocialAuthProvider getProvider(String source) {
212
+ // providers Map 的 key 是 Bean 名称,需要匹配 source
213
+ return providers.values().stream()
214
+ .filter(p -> p.getSource().equals(source))
215
+ .findFirst()
216
+ .orElseThrow(() -> new [你的异常类]("不支持的登录平台: " + source));
217
+ }
189
218
  }
190
219
  ```
191
220
 
192
- ---
193
-
194
- ## 五、前端集成
221
+ ### 四、绑定表设计
222
+
223
+ ```sql
224
+ CREATE TABLE sys_social_bind (
225
+ id BIGINT NOT NULL COMMENT '主键',
226
+ user_id BIGINT NOT NULL COMMENT '系统用户ID',
227
+ auth_id VARCHAR(128) NOT NULL COMMENT '唯一标识 (source:openId)',
228
+ source VARCHAR(32) NOT NULL COMMENT '来源平台',
229
+ open_id VARCHAR(128) NOT NULL COMMENT '平台用户ID',
230
+ nickname VARCHAR(64) COMMENT '昵称',
231
+ avatar VARCHAR(512) COMMENT '头像',
232
+ email VARCHAR(128) COMMENT '邮箱',
233
+ created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
234
+ PRIMARY KEY (id),
235
+ UNIQUE KEY uk_auth_id (auth_id),
236
+ KEY idx_user_id (user_id)
237
+ );
238
+ ```
195
239
 
196
- ```javascript
197
- // 跳转授权
198
- const { data } = await request.get(`/auth/binding/${source}`);
199
- window.location.href = data;
240
+ ### 五、配置
200
241
 
201
- // 回调页面处理
202
- const { source, code, state } = this.$route.query;
203
- const { data } = await request.post('/auth/login', {
204
- grantType: 'social',
205
- source, socialCode: code, socialState: state,
206
- clientId: 'your-client-id'
207
- });
208
- setToken(data.accessToken);
242
+ ```yaml
243
+ social:
244
+ github:
245
+ client-id: ${GITHUB_CLIENT_ID:}
246
+ client-secret: ${GITHUB_CLIENT_SECRET:}
247
+ redirect-uri: https://your-domain.com/social-callback?source=github
248
+ wechat:
249
+ app-id: ${WECHAT_APP_ID:}
250
+ app-secret: ${WECHAT_APP_SECRET:}
251
+ redirect-uri: https://your-domain.com/social-callback?source=wechat_open
209
252
  ```
210
253
 
211
254
  ---
212
255
 
213
- ## 六、常见错误
256
+ ## 选型建议
214
257
 
215
- ```yaml
216
- # ❌ 回调地址与第三方平台配置不一致
217
- redirect-uri: http://localhost:8080/callback
258
+ | 方案 | 优点 | 缺点 | 适用场景 |
259
+ |------|------|------|---------|
260
+ | 自研(如上) | 完全可控、无依赖 | 每个平台需手动对接 | 接入 1-3 个平台 |
261
+ | JustAuth | 开箱即用、20+ 平台 | 引入第三方依赖 | 多平台快速接入 |
262
+ | Spring Security OAuth2 Client | Spring 生态原生 | 配置复杂 | 企业级、标准 OAuth2 |
218
263
 
219
- # ✅ 使用与第三方平台一致的地址
220
- redirect-uri: https://your-domain.com/social-callback?source=github
221
- ```
264
+ ### 常见平台接入
265
+
266
+ | 平台 | 标识 | 特殊要求 |
267
+ |------|------|---------|
268
+ | GitHub | `github` | 无 |
269
+ | 微信开放平台 | `wechat_open` | 需企业开发者认证 |
270
+ | 微信公众号 | `wechat_mp` | 需服务号 |
271
+ | QQ | `qq` | 需备案域名 |
272
+ | 钉钉 | `dingtalk` | 需创建 H5 微应用 |
273
+ | 企业微信 | `wechat_enterprise` | 需 agentId |
274
+ | 支付宝 | `alipay` | 需应用公钥/私钥 |
275
+
276
+ ---
277
+
278
+ ## 常见错误
222
279
 
223
280
  ```java
224
- // 不检查响应结果
225
- AuthUser user = response.getData(); // 可能 null
281
+ // 1. 不校验 state 参数(CSRF 攻击风险)
282
+ SocialUser user = provider.authenticate(code, state);
283
+ // 应先从 Redis 校验 state 是否有效
226
284
 
227
- // 先检查状态
228
- if (!response.ok()) throw new ServiceException(response.getMsg());
285
+ // 2. 回调地址与第三方平台配置不一致
286
+ // 应确保 redirect_uri 与第三方平台配置完全一致(包括协议、域名、路径、参数)
229
287
 
230
- // source 标识拼写错误
231
- SocialUtils.getAuthRequest("wechat", props); // 不存在
288
+ // 3. 不检查认证响应
289
+ SocialUser user = provider.authenticate(code, state);
290
+ user.getOpenId(); // 认证可能失败,user 为 null 或字段缺失
291
+ // 应先检查认证结果
232
292
 
233
- // 正确标识
234
- SocialUtils.getAuthRequest("wechat_open", props); // 微信开放平台
235
- SocialUtils.getAuthRequest("wechat_mp", props); // 微信公众号
236
- ```
293
+ // 4. source 标识拼写错误
294
+ getProvider("wechat"); // 不存在
295
+ getProvider("wechat_open"); // 正确
237
296
 
238
- ---
297
+ // 5. 绑定关系不检查冲突
298
+ // 同一个第三方账号被多个系统账号绑定
299
+ // authId 应设为唯一索引
239
300
 
240
- ## 七、扩展自定义平台
301
+ // 6. 未处理 Token 过期
302
+ // 第三方 access_token 有有效期,需要用 refresh_token 刷新
303
+ // 或每次登录重新获取
241
304
 
242
- ```java
243
- public class AuthCustomRequest extends AuthDefaultRequest {
244
- public AuthCustomRequest(AuthConfig config, AuthStateCache stateCache) {
245
- super(config, AuthCustomSource.CUSTOM, stateCache);
246
- }
247
- @Override
248
- protected AuthToken getAccessToken(AuthCallback authCallback) { ... }
249
- @Override
250
- protected AuthUser getUserInfo(AuthToken authToken) { ... }
251
- }
252
- // 然后在 SocialUtils.getAuthRequest() 中添加对应 case
305
+ // 7. 前端直接传 client_secret
306
+ // client_secret 只能在后端使用,绝不能暴露给前端
253
307
  ```
254
308
 
255
- ---
309
+ ### 前端集成参考
256
310
 
257
- ## 八、参考代码位置
311
+ ```javascript
312
+ // 跳转授权
313
+ const { data: authorizeUrl } = await request.get(`/auth/social/authorize/${source}`);
314
+ window.location.href = authorizeUrl;
258
315
 
259
- | 类型 | 位置 |
260
- |------|------|
261
- | SocialUtils | `ruoyi-common/ruoyi-common-social/.../utils/SocialUtils.java` |
262
- | AuthRedisStateCache | `ruoyi-common/ruoyi-common-social/.../utils/AuthRedisStateCache.java` |
263
- | SocialProperties | `ruoyi-common/ruoyi-common-social/.../config/properties/SocialProperties.java` |
264
- | SocialAuthStrategy | `ruoyi-admin/.../web/service/impl/SocialAuthStrategy.java` |
265
- | AuthController | `ruoyi-admin/.../web/controller/AuthController.java` |
266
- | ISysSocialService | `ruoyi-modules/ruoyi-system/.../service/ISysSocialService.java` |
316
+ // 回调页面处理
317
+ const { source, code, state } = getQueryParams();
318
+ const { data } = await request.post('/auth/social/callback', {
319
+ source, code, state
320
+ });
321
+ if (data.token) {
322
+ setToken(data.token);
323
+ router.push('/');
324
+ } else {
325
+ // 引导绑定或注册
326
+ router.push({ path: '/bindAccount', query: { source, code, state } });
327
+ }
328
+ ```
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: sync-back-merge
3
+ description: |
4
+ 合并 sync-back Issue 到源仓库的标准流程。当维护者需要处理由 `npx ai-engineering-init sync-back --submit` 自动创建的 GitHub Issue 时使用。
5
+ 触发场景:合并 sync-back Issue、处理技能改进反馈、合并用户提交的技能修改。
6
+ ---
7
+
8
+ # sync-back Issue 合并流程
9
+
10
+ 将用户通过 `sync-back --submit` 提交的技能改进 Issue 合并到源仓库。
11
+
12
+ ## 流程步骤
13
+
14
+ ### 1. 查看待处理 Issue
15
+
16
+ ```bash
17
+ gh issue list --repo xu-cell/ai-engineering-init --label "" --search "[sync-back]" --state open
18
+ ```
19
+
20
+ ### 2. 读取 Issue 内容
21
+
22
+ ```bash
23
+ gh issue view <ISSUE_NUMBER> --repo xu-cell/ai-engineering-init
24
+ ```
25
+
26
+ 从 Issue 中提取:
27
+ - 修改的技能名称列表
28
+ - 每个技能的 diff 内容(`---/+++/@@ @@` 格式)
29
+
30
+ ### 3. 审查 diff
31
+
32
+ 逐个检查每个技能的改动:
33
+ - 内容是否合理、准确
34
+ - 是否符合项目规范
35
+ - 是否有安全风险
36
+
37
+ ### 4. 应用改动到源目录
38
+
39
+ **关键**:改动必须应用到 `src/skills/<skill-name>/SKILL.md`(单一源),不要直接改 `.claude/` 或 `.cursor/`。
40
+
41
+ 根据 diff 中的 `@@ @@` 行号和上下文,用 Edit 工具逐个修改对应的源文件。
42
+
43
+ ### 5. 构建同步
44
+
45
+ ```bash
46
+ npm run build:skills
47
+ ```
48
+
49
+ 自动同步到 `.claude/skills/`、`.cursor/skills/`、`.codex/skills/` 三个平台目录。
50
+
51
+ ### 6. 关闭 Issue
52
+
53
+ ```bash
54
+ gh issue close <ISSUE_NUMBER> --repo xu-cell/ai-engineering-init --comment "已合并到 src/skills/,将在下个版本发布。"
55
+ ```
56
+
57
+ ### 7. 提交代码(可选)
58
+
59
+ 如用户要求提交:
60
+
61
+ ```bash
62
+ git add src/skills/<skill-name>/SKILL.md .claude/skills/ .cursor/skills/ .codex/skills/
63
+ git commit -m "feat(skills): 合并 sync-back 技能改进 (#<ISSUE_NUMBER>)"
64
+ ```
65
+
66
+ 如用户要求发版,按 `RELEASE.md` 流程操作。