diff --git a/README.md b/README.md index b74f52e5..5b8435d8 100644 --- a/README.md +++ b/README.md @@ -199,13 +199,14 @@ | `mf_system.sql` | 系统管理数据库 | | `mf_scheduler.sql` | 调度中心数据库 | | `mf_nocode.sql` | 低代码中心数据库 | +| `mf_workflow.sql` | 工作流数据库 | | `mf_demo.sql` | 样例中心数据库(非必须) | | `mfish-nocode.sql` | 单实例数据库
`单实例启动只需要执行这个脚本` | ``` 如果单实例使用只需要导入mfish_nocode.sql库即可 如果使用微服务需要导入mf_config.sql、mf_oauth.sql -、mf_system.sql、mf_scheduler.sql、mf_nocode.sql、mf_demo.sql等数据库 +、mf_system.sql、mf_scheduler.sql、mf_nocode.sql、mf_workflow.sql、mf_demo.sql等数据库 ``` #### 单实例启动 * 初始化SQL脚本 mfish-nocode.sql diff --git a/db/mf_config.sql b/db/mf_config.sql index b83499a8..05e3f992 100644 --- a/db/mf_config.sql +++ b/db/mf_config.sql @@ -34,8 +34,8 @@ CREATE TABLE `config_info` ( -- Records of config_info -- ---------------------------- INSERT INTO `config_info` VALUES (1, 'application-dev.yml', 'DEFAULT_GROUP', 'spring:\n data:\n redis:\n host: localhost\n port: 6379\n password: \"1qaz@WSX\"\n database: 5\n timeout: 10s\n lettuce:\n pool:\n # 连接池中的最小空闲连接\n min-idle: 0\n # 连接池中的最大空闲连接\n max-idle: 8\n # 连接池的最大数据库连接数\n max-active: 8\n # #连接池最大阻塞等待时间(使用负值表示没有限制)\n max-wait: -1ms\n servlet:\n multipart:\n # 单个文件大小\n max-file-size: 1200MB\n # 设置总上传的文件大小\n max-request-size: 1200MB\n ai:\n openai:\n api-key: sk-or-v1-b4**************************************a99b135dae3b\n base-url: https://openrouter.ai/api\n chat:\n options:\n model: meta-llama/llama-3.3-70b-instruct:free\n max-tokens: 1000\n temperature: 0.7\n ollama:\n base-url: http://localhost:11434\n chat:\n model: qwen3:8b\n# feign 配置\nfeign:\n sentinel:\n enabled: true\n okhttp: \n enabled: true\n httpclient:\n enabled: false\n client:\n config:\n default:\n connectTimeout: 10000\n readTimeout: 10000\n compression:\n request:\n enabled: false\n response:\n enabled: false\n\n# 暴露监控端点\nmanagement:\n health:\n sentinel:\n # 关闭健康检查,避免日志刷屏\n enabled: false\n endpoints:\n web:\n exposure:\n include: \'*\'\n endpoint:\n health:\n show-details: always\nlogging:\n config: classpath:logback-logstash.xml\nlog:\n level: info\n maxSize: 30MB\n# 临时缓存存储时间(单位:天)\ncache:\n temp:\n time: 7\nDBConnect:\n password:\n privateKey: 54f0e36b98cf63c3bc6185b61c6e4f2a6e1df3bdeb293a50d7b0bc66881c2419\nswagger:\n title: \'摸鱼低代码平台\'\n enabled: true\n description: 摸鱼框架接口文档\n license: Powered By mfish\n licenseUrl: http://www.mfish.com.cn\n version: 版本号:V2.1.1\n termsOfServiceUrl: http://www.mfish.com.cn\n ignoreServers: mf-gateway,mf-monitor\n contact:\n name: mfish\n email: qiufeng9862@qq.com\n url: http://www.mfish.com.cn\nspringdoc:\n swagger-ui:\n enabled: true\n path: /swagger-ui.html\n tags-sorter: alpha\n operations-sorter: alpha\n default-models-expand-depth: -1\n docExpansion: none\n api-docs:\n enabled: true\n path: /v3/api-docs\n use-management-port: false\n default-flat-param-object: true', '7e1f6a2d2b02f7b666445e626043d9f5', '2023-01-11 12:10:11', '2025-09-05 15:25:07', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); -INSERT INTO `config_info` VALUES (2, 'mf-gateway-dev.yml', 'DEFAULT_GROUP', 'spring:\n cloud:\n gateway:\n server:\n webflux:\n globalcors:\n cors-configurations:\n \'[/**]\':\n max-age: 3600\n allowed-origin-patterns: \"*\"\n allowed-headers: \"*\"\n allow-credentials: true\n allowed-methods:\n - GET\n - POST\n - DELETE\n - PUT\n - OPTION \n discovery:\n locator:\n lowerCaseServiceId: true\n enabled: true\n routes:\n # 认证中心\n - id: oauth2\n uri: lb://mf-oauth\n predicates:\n - Path=/oauth2/**\n filters:\n - CacheFilter\n # 验证码处理\n - CheckCodeFilter\n - StripPrefix=1\n # 测试中心\n - id: test\n uri: lb://mf-test\n predicates:\n - Path=/test/**\n filters:\n - StripPrefix=1\n - id: sys\n uri: lb://mf-sys\n predicates:\n - Path=/sys/**\n filters:\n - StripPrefix=1\n - id: demo\n uri: lb://mf-demo\n predicates:\n - Path=/demo/**\n filters:\n - StripPrefix=1\n - id: storage\n uri: lb://mf-storage\n predicates:\n - Path=/storage/**\n filters:\n - StripPrefix=1\n - id: scheduler\n uri: lb://mf-scheduler\n predicates:\n - Path=/scheduler/**\n filters:\n - StripPrefix=1\n - id: nocode\n uri: lb://mf-nocode\n predicates:\n - Path=/nocode/**\n filters:\n - StripPrefix=1\n - id: workflow\n uri: lb://mf-workflow\n predicates:\n - Path=/workflow/**\n filters:\n - StripPrefix=1\n - id: ai\n uri: lb://mf-ai\n predicates:\n - Path=/ai/**\n filters:\n - StripPrefix=1\n# 安全配置\nsecurity:\n # 验证码\n captcha:\n enabled: true\n type: math\n # 网关校验直接返回\n gatewayCheckCaptcha:\n # - /oauth2/authorize\n # 网关校验,自己服务返回\n selfCheckCaptcha:\n - /oauth2/authorize?loginType=user_password\n # 防止XSS攻击\n xss:\n enabled: true\n excludeUrls:\n # 不校验白名单\n ignore:\n whites:\n - /oauth2/user/revoke\n - /oauth2/accessToken\n - /oauth2/authorize\n - /oauth2/sendMsg\n - /oauth2/qrCodeLogin/*\n - /oauth2/wx/bind/**\n - /oauth2/static/**\n - /oauth2/gitee/**\n - /oauth2/github/**\n - /storage/file/*.*\n - /code/**\n - /test/**\n - /*/v3/api-docs\n# swagger configuration\nspringdoc: \n webjars: \n # 设置为空,不要前缀,不配置这个就需要多加一层路径`/webjars`->`http:host:网关ip/webjars/swagger-ui/index.html`\n prefix:', '038e03dabf17cbb6a5e170d24b10ce04', '2023-01-11 12:10:11', '2025-09-28 22:48:56', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); -INSERT INTO `config_info` VALUES (3, 'mf-oauth-dev.yml', 'DEFAULT_GROUP', 'swagger:\n title: \'摸鱼认证中心\'\n prefix: /oauth2\n# mybatis配置\nmybatis-plus:\n # 搜索指定包别名\n type-aliases-package: cn.com.mfish.oauth.mapper\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\n mapper-locations: classpath*:cn/com/mfish/**/mapper/**/*Mapper.xml\n global-config:\n # 关闭MP3.0自带的banner\n banner: false\n db-config:\n #主键类型 0:\"数据库ID自增\",1:\"该类型为未设置主键类型\", 2:\"用户输入ID\",3:\"全局唯一ID (数字类型唯一ID)\", 4:\"全局唯一ID UUID\",5:\"字符串全局唯一ID (idWorker 的字符串表示)\";\n #id-type: 0\n # 默认数据库表下划线命名\n table-underline: true\n #逻辑删除 删除前\n logic-not-delete-value: 0\n #逻辑删除 删除后\n logic-delete-value: 1\n configuration:\n # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用\n #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\n # 返回类型为Map,显示null对应的字段\n call-setters-on-nulls: true\n map-underscore-to-camel-case: true\n auto-mapping-behavior: full\n jdbc-type-for-null:\nspring: \n thymeleaf:\n cache: false\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,slf4j\n connectionProperties: druid.stat.mergeSql\\=true;druid.stat.slowSqlMillis\\=5000\n datasource:\n # 主库数据源\n master:\n driver-class-name: com.mysql.cj.jdbc.Driver\n url: jdbc:mysql://localhost:3306/mf_oauth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n username: root\n password: 123456\n # 从库数据源\n # slave:\n # username: \n # password: \n # url: \n # driver-class-name: \n # seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭\nredisCache:\n expire: 50400\n keyPrefix: sso_cache\nredisSession:\n expire: 50400\n keyPrefix: sso_session\noauth2:\n expire:\n code: 180\n token: 21600\n refreshToken: 604800\n login:\n mutex: false\n user:\n autoCreate: false\n token:\n sm4key: 143be1ae6ee10b048f7e441cec2a9803\nwechat:\n miniapp:\n appId: wx*************e34a3\n secret: a65e*************fa222\ngitee:\n clientId: a83ea66522fef5f382c6aaffa875c7e87b21d3933277c44a326cd3407fc0b651\n clientSecret: 3ebdce2afcb4a7df9b16355f60d898d452f4d9cf8ad1ab8279bb3bb465e614db\n redirectUri: http://localhost:5186/giteeOauth2\ngithub:\n clientId: Ov23li7oH0Djw3ZEJCoJ\n clientSecret: ada9a510b3bde542e7510142f07d32a5f6bed131\n redirectUri: http://localhost:5186/githubOauth2', '67911ca64ac75db33987b9490f2bd989', '2023-01-11 12:10:11', '2025-08-15 18:42:36', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); +INSERT INTO `config_info` VALUES (2, 'mf-gateway-dev.yml', 'DEFAULT_GROUP', 'spring:\n cloud:\n gateway:\n server:\n webflux:\n globalcors:\n cors-configurations:\n \'[/**]\':\n max-age: 3600\n allowed-origin-patterns: \"*\"\n allowed-headers: \"*\"\n allow-credentials: true\n allowed-methods:\n - GET\n - POST\n - DELETE\n - PUT\n - OPTION \n discovery:\n locator:\n lowerCaseServiceId: true\n enabled: true\n routes:\n # 认证中心\n - id: oauth2\n uri: lb://mf-oauth\n predicates:\n - Path=/oauth2/**\n filters:\n - CacheFilter\n # 验证码处理\n - CheckCodeFilter\n - StripPrefix=1\n # 测试中心\n - id: test\n uri: lb://mf-test\n predicates:\n - Path=/test/**\n filters:\n - StripPrefix=1\n - id: sys\n uri: lb://mf-sys\n predicates:\n - Path=/sys/**\n filters:\n - StripPrefix=1\n - id: demo\n uri: lb://mf-demo\n predicates:\n - Path=/demo/**\n filters:\n - StripPrefix=1\n - id: storage\n uri: lb://mf-storage\n predicates:\n - Path=/storage/**\n filters:\n - StripPrefix=1\n - id: scheduler\n uri: lb://mf-scheduler\n predicates:\n - Path=/scheduler/**\n filters:\n - StripPrefix=1\n - id: nocode\n uri: lb://mf-nocode\n predicates:\n - Path=/nocode/**\n filters:\n - StripPrefix=1\n - id: workflow\n uri: lb://mf-workflow\n predicates:\n - Path=/workflow/**\n filters:\n - StripPrefix=1\n - id: ai\n uri: lb://mf-ai\n predicates:\n - Path=/ai/**\n filters:\n - StripPrefix=1\n# 安全配置\nsecurity:\n # 验证码\n captcha:\n enabled: true\n type: math\n # 网关校验直接返回\n gatewayCheckCaptcha:\n # - /oauth2/authorize\n # 网关校验,自己服务返回\n selfCheckCaptcha:\n - /oauth2/authorize?loginType=user_password\n # 防止XSS攻击\n xss:\n enabled: true\n excludeUrls:\n # 不校验白名单\n ignore:\n whites:\n - /oauth2/user/revoke\n - /oauth2/accessToken\n - /oauth2/authorize\n - /oauth2/sendMsg\n - /oauth2/qrCodeLogin/*\n - /oauth2/wx/bind/**\n - /oauth2/static/**\n - /oauth2/gitee/token/**\n - /oauth2/gitee/url\n - /oauth2/github/token/**\n - /oauth2/github/url\n - /storage/file/*.*\n - /code/**\n - /test/**\n - /*/v3/api-docs\n# swagger configuration\nspringdoc: \n webjars: \n # 设置为空,不要前缀,不配置这个就需要多加一层路径`/webjars`->`http:host:网关ip/webjars/swagger-ui/index.html`\n prefix:', '16d5ac795ab719bf5027706cc64dcae8', '2023-01-11 12:10:11', '2025-10-23 00:07:13', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); +INSERT INTO `config_info` VALUES (3, 'mf-oauth-dev.yml', 'DEFAULT_GROUP', 'swagger:\n title: \'摸鱼认证中心\'\n prefix: /oauth2\n# mybatis配置\nmybatis-plus:\n # 搜索指定包别名\n type-aliases-package: cn.com.mfish.oauth.mapper\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\n mapper-locations: classpath*:cn/com/mfish/**/mapper/**/*Mapper.xml\n global-config:\n # 关闭MP3.0自带的banner\n banner: false\n db-config:\n #主键类型 0:\"数据库ID自增\",1:\"该类型为未设置主键类型\", 2:\"用户输入ID\",3:\"全局唯一ID (数字类型唯一ID)\", 4:\"全局唯一ID UUID\",5:\"字符串全局唯一ID (idWorker 的字符串表示)\";\n #id-type: 0\n # 默认数据库表下划线命名\n table-underline: true\n #逻辑删除 删除前\n logic-not-delete-value: 0\n #逻辑删除 删除后\n logic-delete-value: 1\n configuration:\n # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用\n #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\n # 返回类型为Map,显示null对应的字段\n call-setters-on-nulls: true\n map-underscore-to-camel-case: true\n auto-mapping-behavior: full\n jdbc-type-for-null:\nspring: \n thymeleaf:\n cache: false\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,slf4j\n connectionProperties: druid.stat.mergeSql\\=true;druid.stat.slowSqlMillis\\=5000\n datasource:\n # 主库数据源\n master:\n driver-class-name: com.mysql.cj.jdbc.Driver\n url: jdbc:mysql://localhost:3306/mf_oauth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n username: root\n password: 123456\n # 从库数据源\n # slave:\n # username: \n # password: \n # url: \n # driver-class-name: \n # seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭\nredisCache:\n expire: 50400\n keyPrefix: sso_cache\nredisSession:\n expire: 50400\n keyPrefix: sso_session\noauth2:\n expire:\n code: 180\n token: 21600\n refreshToken: 604800\n login:\n mutex: false\n user:\n autoCreate: false\n token:\n sm4key: 143be1ae6ee10b048f7e441cec2a9803\nwechat:\n miniapp:\n appId: wx*************e34a3\n secret: a65e*************fa222\ngitee:\n clientId: a83ea66522fef5f382c6aaffa875c7e87b21d3933277c44a326cd3407fc0b651\n clientSecret: 3ebdce2afcb4a7df9b16355f60d898d452f4d9cf8ad1ab8279bb3bb465e614db\n redirectUri: http://localhost:5186/giteeOauth2\n bindUri: http://localhost:5186/tenant/info/1?callback=gitee\ngithub:\n clientId: Ov23li7oH0Djw3ZEJCoJ\n clientSecret: ada9a510b3bde542e7510142f07d32a5f6bed131\n redirectUri: http://localhost:5186/githubOauth2\n bindUri: http://localhost:5186/tenant/info/1?callback=github', 'b8bae3fbb48e0e5ccb625f6d47ed99be', '2023-01-11 12:10:11', '2025-10-23 00:09:57', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); INSERT INTO `config_info` VALUES (4, 'sentinel-mf-gateway', 'DEFAULT_GROUP', '[\n {\n \"resource\": \"mf-oauth\",\n \"count\": 500,\n \"grade\": 1,\n \"limitApp\": \"default\",\n \"strategy\": 0,\n \"controlBehavior\": 0\n },\n {\n \"resource\": \"mf-sys\",\n \"count\": 500,\n \"grade\": 1,\n \"limitApp\": \"default\",\n \"strategy\": 0,\n \"controlBehavior\": 0\n },\n {\n \"resource\": \"mf-storage\",\n \"count\": 100,\n \"grade\": 1,\n \"limitApp\": \"default\",\n \"strategy\": 0,\n \"controlBehavior\": 0\n },\n {\n \"resource\": \"mf-scheduler\",\n \"count\": 100,\n \"grade\": 1,\n \"limitApp\": \"default\",\n \"strategy\": 0,\n \"controlBehavior\": 0\n }\n]', '07a23c3f1d6580115132bd0ba7897504', '2023-01-11 12:10:11', '2024-01-31 23:07:56', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'text', '', ''); INSERT INTO `config_info` VALUES (6, 'mf-sys-dev.yml', 'DEFAULT_GROUP', 'swagger:\n title: \'摸鱼系统业务中心\'\n prefix: /sys\n# mybatis配置\nmybatis-plus:\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\n mapper-locations: classpath*:cn/com/mfish/sys/mapper/**/*Mapper.xml\n global-config:\n # 关闭MP3.0自带的banner\n banner: false\n db-config:\n #主键类型 0:\"数据库ID自增\",1:\"该类型为未设置主键类型\", 2:\"用户输入ID\",3:\"全局唯一ID (数字类型唯一ID)\", 4:\"全局唯一ID UUID\",5:\"字符串全局唯一ID (idWorker 的字符串表示)\";\n #id-type: 0\n # 默认数据库表下划线命名\n table-underline: true\n #逻辑删除 删除前\n logic-not-delete-value: 0\n #逻辑删除 删除后\n logic-delete-value: 1\n configuration:\n # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用\n #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\n # 返回类型为Map,显示null对应的字段\n call-setters-on-nulls: true\n map-underscore-to-camel-case: true\n auto-mapping-behavior: full\n jdbc-type-for-null:\nspring: \n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,slf4j\n connectionProperties: druid.stat.mergeSql\\=true;druid.stat.slowSqlMillis\\=5000\n datasource:\n # 主库数据源\n master:\n driver-class-name: com.mysql.cj.jdbc.Driver\n url: jdbc:mysql://localhost:3306/mf_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n username: root\n password: 123456\n # 从库数据源\n # slave:\n # username: \n # password: \n # url: \n # driver-class-name: \n # seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭\ncode:\n template:\n keys:\n - src/main/java/${packageName}/controller/${entityName}Controller.java.ftl\n - src/main/java/${packageName}/entity/${entityName}.java.ftl\n - src/main/java/${packageName}/mapper/${entityName}Mapper.java.ftl\n - src/main/java/${packageName}/mapper/xml/${entityName}Mapper.xml.ftl\n - src/main/java/${packageName}/req/Req${entityName}.java.ftl\n - src/main/java/${packageName}/service/${entityName}Service.java.ftl\n - src/main/java/${packageName}/service/impl/${entityName}ServiceImpl.java.ftl\n - mfish-nocode-view/src/views/${apiPrefix}/${entityName#kebab-case}/${entityName#uncap_first}.data.ts.ftl\n - mfish-nocode-view/src/views/${apiPrefix}/${entityName#kebab-case}/${entityName}Modal.vue.ftl\n - mfish-nocode-view/src/views/${apiPrefix}/${entityName#kebab-case}/${entityName}ViewModal.vue.ftl\n - mfish-nocode-view/src/views/${apiPrefix}/${entityName#kebab-case}/index.vue.ftl\n - mfish-nocode-view/src/api/${apiPrefix}/model/${entityName}Model.ts.ftl\n - mfish-nocode-view/src/api/${apiPrefix}/${entityName}.ts.ftl\n path: template\n savePath:\n #前端地址\n front: E:/webcode\n #后端地址(需要指定到具体模块路径)\n back: E:/javacode/mfish-nocode-pro/mf-business/mf-sys', 'f2b7082dc14fdc4d10c54cc556d23dba', '2023-01-11 12:10:11', '2025-08-15 18:42:23', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); INSERT INTO `config_info` VALUES (7, 'mf-storage-dev.yml', 'DEFAULT_GROUP', 'swagger:\n title: \'摸鱼文件管理\'\n prefix: /storage\n# mybatis配置\nmybatis-plus:\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\n mapper-locations: classpath*:cn/com/mfish/storage/mapper/**/*Mapper.xml\n global-config:\n # 关闭MP3.0自带的banner\n banner: false\n db-config:\n #主键类型 0:\"数据库ID自增\",1:\"该类型为未设置主键类型\", 2:\"用户输入ID\",3:\"全局唯一ID (数字类型唯一ID)\", 4:\"全局唯一ID UUID\",5:\"字符串全局唯一ID (idWorker 的字符串表示)\";\n #id-type: 0\n # 默认数据库表下划线命名\n table-underline: true\n #逻辑删除 删除前\n logic-not-delete-value: 0\n #逻辑删除 删除后\n logic-delete-value: 1\n configuration:\n # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用\n #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\n # 返回类型为Map,显示null对应的字段\n call-setters-on-nulls: true\n map-underscore-to-camel-case: true\n auto-mapping-behavior: full\n jdbc-type-for-null:\nspring: \n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,slf4j\n connectionProperties: druid.stat.mergeSql\\=true;druid.stat.slowSqlMillis\\=5000\n datasource:\n # 主库数据源\n master:\n driver-class-name: com.mysql.cj.jdbc.Driver\n url: jdbc:mysql://localhost:3306/mf_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n username: root\n password: 123456\n # 从库数据源\n # slave:\n # username: \n # password: \n # url: \n # driver-class-name: \n # seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭\nstorage:\n active: local\n address: http://localhost:8888/storage/file/\n local:\n storagePath: E:/storage\n aliyun:\n endpoint: oss-cn-hangzhou.aliyuncs.com\n accessKeyId: LTAI5***********FsgT\n accessKeySecret: ZtZGF***************JADjS2f\n bucketName: landwise\n qiniu:\n domain: http://cdn.mfish.com.cn\n accessKey: cHlX4*******************cdsexgvP\n secretKey: QPACH*******************1vEa1t4S\n bucket: mfish-nocode', 'a7db423b2fa6fc36f58379d97fc7a0e5', '2023-01-11 12:10:11', '2025-08-15 18:42:08', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', ''); diff --git a/db/mf_oauth.sql b/db/mf_oauth.sql index 2e4041fc..13fca429 100644 --- a/db/mf_oauth.sql +++ b/db/mf_oauth.sql @@ -168,6 +168,7 @@ INSERT INTO `sso_menu` VALUES ('442f89857faa6bc929ef4f422b8c4b99', '74ce27a1e940 INSERT INTO `sso_menu` VALUES ('520873645d565776988d81481d8b0d26', '74ce27a1e94091da2efa714e32680e7a', '000120000100003', 3, '修改', '#', 3, 2, '', NULL, 'sys:ssoTenant:query,sys:ssoTenant:update', 0, 1, NULL, NULL, '', 'mfish', '2023-06-28 13:02:59', '', NULL); INSERT INTO `sso_menu` VALUES ('d9832cd3aefbb5f99267edb995ff8c75', '74ce27a1e94091da2efa714e32680e7a', '000120000100004', 3, '删除', '#', 4, 2, '', NULL, 'sys:ssoTenant:query,sys:ssoTenant:delete', 0, 1, NULL, NULL, '', 'mfish', '2023-06-28 13:03:15', '', NULL); INSERT INTO `sso_menu` VALUES ('eb5f513d5430597d3ea312e1bf760b23', '40bf4846599bf8dbd307f77bf51a7dad', '0001200002', 2, '租户信息', 'ant-design:info-circle-outlined', 2, 1, '/info/3', '/sys/account/setting/index.vue', NULL, 0, 1, NULL, 1, '', 'admin', '2023-06-25 20:21:51', 'admin', '2023-06-25 21:15:54'); +INSERT INTO `sso_menu` VALUES ('8f6ed381a02535791c17521ff0475808', 'eb5f513d5430597d3ea312e1bf760b23', '000120000200001', 3, '查询', '#', 1, 2, '', NULL, 'sys:tenant:query', 0, 1, NULL, 1, '', 'admin', '2025-06-03 13:53:51', '', NULL); INSERT INTO `sso_menu` VALUES ('7e690410346c4d3a1610d85e8c9f906b', '40bf4846599bf8dbd307f77bf51a7dad', '0001200003', 2, '个人信息', 'ion:person', 1, 1, '/info/1', '/sys/account/setting/index.vue', NULL, 0, 1, '0000200002', 1, '', 'admin', '2023-06-29 10:54:29', 'admin', '2024-04-09 17:08:26'); INSERT INTO `sso_menu` VALUES ('6d71e92a5a3712acbcfcc58f65b93f4f', '7e690410346c4d3a1610d85e8c9f906b', '000120000300001', 3, '上传图像', '#', 1, 2, '', NULL, 'sys:file:upload', 0, 1, NULL, NULL, '', 'admin', '2023-06-29 10:54:29', '', NULL); INSERT INTO `sso_menu` VALUES ('604277501ebb1c92d70912c49188eaa5', '7e690410346c4d3a1610d85e8c9f906b', '000120000300002', 3, '密码修改', '#', 2, 2, '', NULL, 'sys:password:update', 0, 1, NULL, 1, '', 'admin', '2024-10-31 22:06:28', '', NULL); @@ -219,7 +220,6 @@ INSERT INTO `sso_menu` VALUES ('29db2a54d3226d1ec2f90c6498033c99', '70943d8248fd INSERT INTO `sso_menu` VALUES ('312d2bc9ac4f1a7d2f2b5a61fa59ff64', '29db2a54d3226d1ec2f90c6498033c99', '000110000600003', 3, '修改', '#', 3, 2, '', NULL, 'nocode:screenCharts:query,nocode:screenCharts:update', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); INSERT INTO `sso_menu` VALUES ('77c042434b5bebf53cd89afbd04ca28a', '29db2a54d3226d1ec2f90c6498033c99', '000110000600001', 3, '查询', '#', 1, 2, '', NULL, 'nocode:screenCharts:query', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); INSERT INTO `sso_menu` VALUES ('d4257d0d8d32697a82718a312e51fcda', '29db2a54d3226d1ec2f90c6498033c99', '000110000600002', 3, '新增', '#', 2, 2, '', NULL, 'nocode:screenCharts:query,nocode:screenCharts:insert', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); -INSERT INTO `sso_menu` VALUES ('8f6ed381a02535791c17521ff0475808', 'eb5f513d5430597d3ea312e1bf760b23', '000120000200001', 3, '查询', '#', 1, 2, '', NULL, 'sys:tenant:query', 0, 1, NULL, 1, '', 'admin', '2025-06-03 13:53:51', '', NULL); INSERT INTO `sso_menu` VALUES ('3a9999ccbdcdc36d69cd0266a6513a05', '', '00014', 1, '审批管理', 'ant-design:audit-outlined', 9, 0, '/audit', NULL, NULL, 0, 1, NULL, 1, '', 'admin', '2025-10-15 15:47:38', 'admin', '2025-10-16 10:36:51'); INSERT INTO `sso_menu` VALUES ('e7a2fcb9e2eb633d4008c7f295fffc65', '3a9999ccbdcdc36d69cd0266a6513a05', '0001400001', 2, '审批列表', 'ant-design:file-search-outlined', 1, 1, '/list', '/workflow/audit/index.vue', NULL, 0, 1, NULL, 0, '', 'admin', '2025-10-15 15:47:59', 'admin', '2025-10-17 11:16:41'); INSERT INTO `sso_menu` VALUES ('8a47a373c5b79c7fa495f1abe06efa5d', '3a9999ccbdcdc36d69cd0266a6513a05', '0001400002', 2, '我的申请', 'ant-design:solution-outlined', 2, 1, '/apply', '/workflow/apply/index.vue', NULL, 0, 1, NULL, 1, '', 'admin', '2025-10-17 11:12:30', 'admin', '2025-10-17 11:14:15'); diff --git a/db/mfish_nocode.sql b/db/mfish_nocode.sql index b208ea1f..f58e04f4 100644 --- a/db/mfish_nocode.sql +++ b/db/mfish_nocode.sql @@ -502,6 +502,7 @@ INSERT INTO `sso_menu` VALUES ('442f89857faa6bc929ef4f422b8c4b99', '74ce27a1e940 INSERT INTO `sso_menu` VALUES ('520873645d565776988d81481d8b0d26', '74ce27a1e94091da2efa714e32680e7a', '000120000100003', 3, '修改', '#', 3, 2, '', NULL, 'sys:ssoTenant:query,sys:ssoTenant:update', 0, 1, NULL, NULL, '', 'mfish', '2023-06-28 13:02:59', '', NULL); INSERT INTO `sso_menu` VALUES ('d9832cd3aefbb5f99267edb995ff8c75', '74ce27a1e94091da2efa714e32680e7a', '000120000100004', 3, '删除', '#', 4, 2, '', NULL, 'sys:ssoTenant:query,sys:ssoTenant:delete', 0, 1, NULL, NULL, '', 'mfish', '2023-06-28 13:03:15', '', NULL); INSERT INTO `sso_menu` VALUES ('eb5f513d5430597d3ea312e1bf760b23', '40bf4846599bf8dbd307f77bf51a7dad', '0001200002', 2, '租户信息', 'ant-design:info-circle-outlined', 2, 1, '/info/3', '/sys/account/setting/index.vue', NULL, 0, 1, NULL, 1, '', 'admin', '2023-06-25 20:21:51', 'admin', '2023-06-25 21:15:54'); +INSERT INTO `sso_menu` VALUES ('8f6ed381a02535791c17521ff0475808', 'eb5f513d5430597d3ea312e1bf760b23', '000120000200001', 3, '查询', '#', 1, 2, '', NULL, 'sys:tenant:query', 0, 1, NULL, 1, '', 'admin', '2025-06-03 13:53:51', '', NULL); INSERT INTO `sso_menu` VALUES ('7e690410346c4d3a1610d85e8c9f906b', '40bf4846599bf8dbd307f77bf51a7dad', '0001200003', 2, '个人信息', 'ion:person', 1, 1, '/info/1', '/sys/account/setting/index.vue', NULL, 0, 1, '0000200002', 1, '', 'admin', '2023-06-29 10:54:29', 'admin', '2024-04-09 17:08:26'); INSERT INTO `sso_menu` VALUES ('6d71e92a5a3712acbcfcc58f65b93f4f', '7e690410346c4d3a1610d85e8c9f906b', '000120000300001', 3, '上传图像', '#', 1, 2, '', NULL, 'sys:file:upload', 0, 1, NULL, NULL, '', 'admin', '2023-06-29 10:54:29', '', NULL); INSERT INTO `sso_menu` VALUES ('604277501ebb1c92d70912c49188eaa5', '7e690410346c4d3a1610d85e8c9f906b', '000120000300002', 3, '密码修改', '#', 2, 2, '', NULL, 'sys:password:update', 0, 1, NULL, 1, '', 'admin', '2024-10-31 22:06:28', '', NULL); @@ -553,7 +554,6 @@ INSERT INTO `sso_menu` VALUES ('29db2a54d3226d1ec2f90c6498033c99', '70943d8248fd INSERT INTO `sso_menu` VALUES ('312d2bc9ac4f1a7d2f2b5a61fa59ff64', '29db2a54d3226d1ec2f90c6498033c99', '000110000600003', 3, '修改', '#', 3, 2, '', NULL, 'nocode:screenCharts:query,nocode:screenCharts:update', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); INSERT INTO `sso_menu` VALUES ('77c042434b5bebf53cd89afbd04ca28a', '29db2a54d3226d1ec2f90c6498033c99', '000110000600001', 3, '查询', '#', 1, 2, '', NULL, 'nocode:screenCharts:query', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); INSERT INTO `sso_menu` VALUES ('d4257d0d8d32697a82718a312e51fcda', '29db2a54d3226d1ec2f90c6498033c99', '000110000600002', 3, '新增', '#', 2, 2, '', NULL, 'nocode:screenCharts:query,nocode:screenCharts:insert', 0, 1, NULL, NULL, '', 'admin', '2024-11-19 09:27:16', '', NULL); -INSERT INTO `sso_menu` VALUES ('8f6ed381a02535791c17521ff0475808', 'eb5f513d5430597d3ea312e1bf760b23', '000120000200001', 3, '查询', '#', 1, 2, '', NULL, 'sys:tenant:query', 0, 1, NULL, 1, '', 'admin', '2025-06-03 13:53:51', '', NULL); INSERT INTO `sso_menu` VALUES ('3a9999ccbdcdc36d69cd0266a6513a05', '', '00014', 1, '审批管理', 'ant-design:audit-outlined', 9, 0, '/audit', NULL, NULL, 0, 1, NULL, 1, '', 'admin', '2025-10-15 15:47:38', 'admin', '2025-10-16 10:36:51'); INSERT INTO `sso_menu` VALUES ('e7a2fcb9e2eb633d4008c7f295fffc65', '3a9999ccbdcdc36d69cd0266a6513a05', '0001400001', 2, '审批列表', 'ant-design:file-search-outlined', 1, 1, '/list', '/workflow/audit/index.vue', NULL, 0, 1, NULL, 0, '', 'admin', '2025-10-15 15:47:59', 'admin', '2025-10-17 11:16:41'); INSERT INTO `sso_menu` VALUES ('8a47a373c5b79c7fa495f1abe06efa5d', '3a9999ccbdcdc36d69cd0266a6513a05', '0001400002', 2, '我的申请', 'ant-design:solution-outlined', 2, 1, '/apply', '/workflow/apply/index.vue', NULL, 0, 1, NULL, 1, '', 'admin', '2025-10-17 11:12:30', 'admin', '2025-10-17 11:14:15'); diff --git a/mf-common/mf-common-core/src/main/java/cn/com/mfish/common/core/utils/http/OkHttpUtils.java b/mf-common/mf-common-core/src/main/java/cn/com/mfish/common/core/utils/http/OkHttpUtils.java index af7c7e8c..e065b153 100644 --- a/mf-common/mf-common-core/src/main/java/cn/com/mfish/common/core/utils/http/OkHttpUtils.java +++ b/mf-common/mf-common-core/src/main/java/cn/com/mfish/common/core/utils/http/OkHttpUtils.java @@ -1,6 +1,7 @@ package cn.com.mfish.common.core.utils.http; import cn.com.mfish.common.core.enums.HttpType; +import cn.com.mfish.common.core.exception.MyRuntimeException; import cn.com.mfish.common.core.web.Result; import com.alibaba.fastjson2.JSON; import lombok.Data; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import javax.net.ssl.*; import java.io.File; import java.io.IOException; +import java.net.SocketTimeoutException; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.X509Certificate; @@ -344,6 +346,9 @@ public class OkHttpUtils { return Result.buildResult(data, code, msg); } return Result.buildResult(null, code, msg); + } catch (SocketTimeoutException se) { + log.error("错误:OkHttp请求超时", se); + throw new MyRuntimeException("Http请求超时"); } catch (IOException e) { log.error("错误:OkHttp请求异常", e); throw e; diff --git a/mf-common/mf-common-oauth/src/main/java/cn/com/mfish/common/oauth/service/SsoUserService.java b/mf-common/mf-common-oauth/src/main/java/cn/com/mfish/common/oauth/service/SsoUserService.java index 73679753..06ae90c3 100644 --- a/mf-common/mf-common-oauth/src/main/java/cn/com/mfish/common/oauth/service/SsoUserService.java +++ b/mf-common/mf-common-oauth/src/main/java/cn/com/mfish/common/oauth/service/SsoUserService.java @@ -55,7 +55,7 @@ public interface SsoUserService extends IService { /** * 通过用户ID获取按钮权限 * - * @param userId 用户id + * @param userId 用户id * @param tenantId 租户id * @return 返回权限集合 */ @@ -80,7 +80,7 @@ public interface SsoUserService extends IService { * 插入用户角色关系 * * @param userId 用户id - * @param roles 角色id集合 + * @param roles 角色id集合 * @return 插入数量 */ int insertUserRole(String userId, List roles); @@ -88,7 +88,7 @@ public interface SsoUserService extends IService { /** * 插入用户组织关系 * - * @param userId 用户id + * @param userId 用户id * @param orgList 组织id集合 * @return 返回插入数量 */ @@ -123,5 +123,52 @@ public interface SsoUserService extends IService { boolean allowChangeAccount(String userId); + /** + * 修改账号 + * + * @param userId 用户id + * @param account 账号 + * @return 返回修改后的用户 + */ Result changeAccount(String userId, String account); + + /** + * 获取用户安全设置 + * + * @param userId 用户id + * @return 返回用户安全设置 + */ + SsoUser getSecureSetting(String userId); + + /** + * 解绑gitee账号 + * + * @param userId 用户id + * @return 返回解绑结果 + */ + Result unbindGitee(String userId); + + /** + * 绑定gitee账号 + * + * @param giteeAccount gitee账号 + * @return 返回绑定结果 + */ + Result bindGitee(String giteeAccount); + + /** + * 解绑github账号 + * + * @param userId 用户id + * @return 返回解绑结果 + */ + Result unbindGithub(String userId); + + /** + * 绑定github账号 + * + * @param githubAccount github账号 + * @return 返回绑定结果 + */ + Result bindGithub(String githubAccount); } diff --git a/mf-oauth/src/main/java/cn/com/mfish/oauth/common/GitLoginInfo.java b/mf-oauth/src/main/java/cn/com/mfish/oauth/common/GitLoginInfo.java new file mode 100644 index 00000000..f86beabb --- /dev/null +++ b/mf-oauth/src/main/java/cn/com/mfish/oauth/common/GitLoginInfo.java @@ -0,0 +1,20 @@ +package cn.com.mfish.oauth.common; + +import lombok.Getter; + +/** + * @description: Git登录信息 + * @author: mfish + * @date: 2025/10/22 + */ +@Getter +public class GitLoginInfo { + private final String token; + private final String gitInfo; + private final String account; + public GitLoginInfo(String token, String account, String gitInfo) { + this.token = token; + this.account = account; + this.gitInfo = gitInfo; + } +} diff --git a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GiteeController.java b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GiteeController.java index 3eaa5ec9..a8fd46d3 100644 --- a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GiteeController.java +++ b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GiteeController.java @@ -5,6 +5,7 @@ import cn.com.mfish.common.core.web.Result; import cn.com.mfish.common.oauth.common.SerConstant; import cn.com.mfish.common.oauth.entity.SsoUser; import cn.com.mfish.common.oauth.service.SsoUserService; +import cn.com.mfish.oauth.common.GitLoginInfo; import cn.com.mfish.oauth.service.LoginService; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; @@ -15,10 +16,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.HashMap; @@ -44,6 +42,8 @@ public class GiteeController { private String clientSecret; @Value("${gitee.redirectUri}") private String redirectUri; + @Value("${gitee.bindUri:http://localhost:5186/tenant/info/1?callback=gitee}") + private String bindUri; @Resource SsoUserService ssoUserService; @@ -53,6 +53,51 @@ public class GiteeController { @Parameter(name = SerConstant.QR_CODE, description = "gitee认证Code", required = true) }) public Result getToken(@PathVariable String code) throws IOException { + Result result = getGiteeAccount(code, redirectUri); + if (!result.isSuccess()) { + return Result.fail(result.getMsg()); + } + String account = result.getData().getAccount(); + String token = result.getData().getToken(); + SsoUser user = ssoUserService.getUserByGitee(account); + // 新用户判断是否关注仓库 + if ((user == null || user.getCreateTime().getTime() > System.currentTimeMillis() - 24 * 3600 * 1000) && !isStarred(token, account)) { + return Result.ok(1, "警告:未关注码云仓库"); + } + Result res = loginService.login(result.getData().getGitInfo(), token, SerConstant.LoginType.Gitee, "system", "false"); + if (!res.isSuccess()) { + return Result.fail(res.getMsg()); + } + return Result.ok(0, "登录成功"); + } + + @GetMapping("/url") + @Operation(summary = "获取gitee url") + public Result getUrl() { + return Result.ok("https://gitee.com/oauth/authorize?client_id=" + clientId + + "&redirect_uri=" + redirectUri + "&response_type=code", "获取地址成功"); + } + + @PutMapping("/bind/{code}") + @Operation(summary = "绑定gitee账号") + public Result bindGitee(@Parameter(name = "code", description = "gitee认证Code", required = true) @PathVariable String code) throws IOException { + Result result = getGiteeAccount(code, bindUri); + if (!result.isSuccess()) { + return Result.fail(result.getMsg()); + } + String account = result.getData().getAccount(); + return ssoUserService.bindGitee(account); + } + + /** + * 获取码云账号信息 + * + * @param code 认证码 + * @param redirectUri 重定向地址 + * @return 码云账号信息 + * @throws IOException 网络异常 + */ + private Result getGiteeAccount(String code, String redirectUri) throws IOException { Map param = new HashMap<>(); param.put("client_secret", clientSecret); Result result = OkHttpUtils.postJson("https://gitee.com/oauth/token?grant_type=authorization_code" + @@ -69,24 +114,7 @@ public class GiteeController { return Result.fail("错误:请求码云用户信息失败"); } JSONObject giteeUser = JSON.parseObject(result.getData()); - String login = giteeUser.getString("login"); - SsoUser user = ssoUserService.getUserByGitee(giteeUser.getString("login")); - // 新用户判断是否关注仓库 - if ((user == null || user.getCreateTime().getTime() > System.currentTimeMillis() - 24 * 3600 * 1000) && !isStarred(token, login)) { - return Result.ok(1, "警告:未关注码云仓库"); - } - result = loginService.login(result.getData(), token, SerConstant.LoginType.Gitee, "system", "false"); - if (!result.isSuccess()) { - return Result.fail(result.getMsg()); - } - return Result.ok(0, "登录成功"); - } - - @GetMapping("/url") - @Operation(summary = "获取gitee url") - public Result getUrl() { - return Result.ok("https://gitee.com/oauth/authorize?client_id=" + clientId - + "&redirect_uri=" + redirectUri + "&response_type=code", "获取地址成功"); + return Result.ok(new GitLoginInfo(token, giteeUser.getString("login"), result.getData()), "获取码云账号成功"); } /** @@ -111,7 +139,7 @@ public class GiteeController { Result result = OkHttpUtils.get("https://gitee.com/api/v5/repos/qiufeng9862/mfish-nocode/events?limit=500&access_token=" + token); if (result.isSuccess()) { Pattern pattern = Pattern.compile("\"StarEvent\".*?\"login\"\\s*:\\s*\"(?.*?)\""); - String data = result.getData().replace(" ",""); + String data = result.getData().replace(" ", ""); Matcher matcher = pattern.matcher(data); while (matcher.find()) { String value = matcher.group("login"); diff --git a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GithubController.java b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GithubController.java index 9e40d166..9a46d713 100644 --- a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GithubController.java +++ b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/GithubController.java @@ -1,8 +1,11 @@ package cn.com.mfish.oauth.controller; +import cn.com.mfish.common.core.utils.StringUtils; import cn.com.mfish.common.core.utils.http.OkHttpUtils; import cn.com.mfish.common.core.web.Result; import cn.com.mfish.common.oauth.common.SerConstant; +import cn.com.mfish.common.oauth.service.SsoUserService; +import cn.com.mfish.oauth.common.GitLoginInfo; import cn.com.mfish.oauth.service.LoginService; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; @@ -12,10 +15,7 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.HashMap; @@ -38,6 +38,10 @@ public class GithubController { private String clientSecret; @Value("${github.redirectUri}") private String redirectUri; + @Value("${github.bindUri:http://localhost:5186/tenant/info/1?callback=github}") + private String bindUri; + @Resource + SsoUserService ssoUserService; @GetMapping("/token/{code}") @Operation(summary = "获取github token") @@ -45,6 +49,47 @@ public class GithubController { @Parameter(name = SerConstant.QR_CODE, description = "github认证Code", required = true) }) public Result getToken(@PathVariable String code) throws IOException { + Result result = getGithubAccount(code, redirectUri); + if (!result.isSuccess()) { + return Result.fail(result.getMsg()); + } + String token = result.getData().getToken(); + if (!isStarred(token)) { + return Result.ok(1, "警告:未关注github仓库"); + } + Result res = loginService.login(result.getData().getGitInfo(), token, SerConstant.LoginType.Github, "system", "false"); + if (!res.isSuccess()) { + return Result.fail(result.getMsg()); + } + return Result.ok(0, "登录成功"); + } + + @GetMapping("/url") + @Operation(summary = "获取github url") + public Result getUrl() { + return Result.ok("https://github.com/login/oauth/authorize?client_id=" + clientId + + "&redirect_uri=" + redirectUri + "&response_type=code", "获取地址成功"); + } + + @PutMapping("/bind/{code}") + @Operation(summary = "绑定github账号") + public Result bindGithub(@Parameter(name = "code", description = "github认证Code", required = true) @PathVariable String code) throws IOException { + Result result = getGithubAccount(code, bindUri); + if (!result.isSuccess()) { + return Result.fail(result.getMsg()); + } + String account = result.getData().getAccount(); + return ssoUserService.bindGithub(account); + } + + /** + * 获取github账号信息 + * + * @param code 认证code + * @param redirectUri 回调地址 + * @return 账号信息 + */ + private Result getGithubAccount(String code, String redirectUri) throws IOException { Map param = new HashMap<>(); param.put("client_id", clientId); param.put("client_secret", clientSecret); @@ -59,28 +104,18 @@ public class GithubController { } jsonObject = JSON.parseObject(result.getData()); String token = jsonObject.getString("access_token"); - if (!isStarred(token)) { - return Result.ok(1, "警告:未关注github仓库"); - } header.clear(); header.put("Authorization", "Bearer " + token); result = OkHttpUtils.get("https://api.github.com/user", header); if (!result.isSuccess()) { return Result.fail("错误:请求github用户信息失败"); } - result = loginService.login(result.getData(), token, SerConstant.LoginType.Github, "system", "false"); - if (!result.isSuccess()) { - return Result.fail(result.getMsg()); + jsonObject = JSON.parseObject(result.getData()); + String account = jsonObject.getString("login"); + if (StringUtils.isEmpty(account)) { + return Result.fail("错误:未获取到账号"); } - return Result.ok(0, "登录成功"); - - } - - @GetMapping("/url") - @Operation(summary = "获取github url") - public Result getUrl() { - return Result.ok("https://github.com/login/oauth/authorize?client_id=" + clientId - + "&redirect_uri=" + redirectUri + "&response_type=code", "获取地址成功"); + return Result.ok(new GitLoginInfo(token, account, result.getData()), "获取github账号成功"); } /** diff --git a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/SsoUserController.java b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/SsoUserController.java index b60b4fb0..ff55d813 100644 --- a/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/SsoUserController.java +++ b/mf-oauth/src/main/java/cn/com/mfish/oauth/controller/SsoUserController.java @@ -329,4 +329,26 @@ public class SsoUserController { public Result changeAccount(@RequestBody SsoUser ssoUser) { return ssoUserService.changeAccount(ssoUser.getId(), ssoUser.getAccount()); } -} + + @Operation(summary = "获取用户安全设置") + @GetMapping("/secureSetting/{userId}") + public Result getSecureSetting(@PathVariable("userId") String userId) { + return Result.ok(ssoUserService.getSecureSetting(userId)); + } + + @Operation(summary = "解绑gitee账号") + @PutMapping("/unbind/gitee/{userId}") + @Log(title = "解绑gitee账号", operateType = OperateType.UPDATE) + public Result unbindGitee(@PathVariable("userId") String userId) { + return ssoUserService.unbindGitee(userId); + } + + + + @Operation(summary = "解绑github账号") + @PutMapping("/unbind/github/{userId}") + @Log(title = "解绑github账号", operateType = OperateType.UPDATE) + public Result unbindGithub(@PathVariable("userId") String userId) { + return ssoUserService.unbindGithub(userId); + } +} \ No newline at end of file diff --git a/mf-oauth/src/main/java/cn/com/mfish/oauth/service/impl/SsoUserServiceImpl.java b/mf-oauth/src/main/java/cn/com/mfish/oauth/service/impl/SsoUserServiceImpl.java index 2c8b8e5e..f1e87fd6 100644 --- a/mf-oauth/src/main/java/cn/com/mfish/oauth/service/impl/SsoUserServiceImpl.java +++ b/mf-oauth/src/main/java/cn/com/mfish/oauth/service/impl/SsoUserServiceImpl.java @@ -503,7 +503,6 @@ public class SsoUserServiceImpl extends ServiceImpl impl if (isAccountExist(account, userId)) { return Result.fail(user, "错误:账号已存在"); } - if (baseMapper.updateById(user) > 0) { userTempCache.removeOneCache(userId); return Result.ok(user, "修改账号成功"); @@ -511,6 +510,123 @@ public class SsoUserServiceImpl extends ServiceImpl impl return Result.fail(user, "修改账号失败"); } + @Override + public SsoUser getSecureSetting(String userId) { + SsoUser ssoUser = userTempCache.getFromCacheAndDB(userId); + //脱敏 + ssoUser.setPassword(null); + ssoUser.setOldPassword(null); + if(ssoUser.getPhone() != null){ + ssoUser.setPhone(ssoUser.getPhone().replaceAll("(\\d{3})(\\d*)(\\d{2})", "$1****$3")); + } + if(ssoUser.getEmail() != null){ + ssoUser.setEmail(ssoUser.getEmail().replaceAll("(\\w)\\w*(@\\w+\\.\\w+)", "$1****$2")); + } + if(ssoUser.getGitee() != null){ + ssoUser.setGitee(ssoUser.getGitee().replaceAll("(\\w{1})(\\w*)(\\w{1})", "$1****$3")); + } + if(ssoUser.getGithub() != null){ + ssoUser.setGithub(ssoUser.getGithub().replaceAll("(\\w{1})(\\w*)(\\w{1})", "$1****$3")); + } + if(ssoUser.getOpenid() != null){ + ssoUser.setOpenid(ssoUser.getOpenid().replaceAll("(\\w{1})(\\w*)(\\w{2})", "$1****$3")); + } + return ssoUser; + } + + @Override + public Result unbindGitee(String userId) { + Result result = unbindVerify(userId); + if(!result.isSuccess()){ + return Result.fail(false, result.getMsg()); + } + SsoUser ssoUser = result.getData(); + if(StringUtils.isEmpty(ssoUser.getGitee())){ + return Result.fail(false, "错误:未绑定gitee账号"); + } + ssoUser.setGitee(""); + if(baseMapper.updateById(ssoUser) > 0){ + userTempCache.removeOneCache(userId); + return Result.ok(true, "解绑gitee账号成功"); + } + return Result.fail(false, "解绑gitee账号失败"); + } + + @Override + public Result bindGitee(String giteeAccount) { + SsoUser user = getUserByGitee(giteeAccount); + // 新用户判断是否关注仓库 + if (user != null) { + return Result.fail(false, "警告:Gitee账号已被其他用户绑定"); + } + String userId = AuthInfoUtils.getCurrentUserId(); + if(StringUtils.isEmpty(userId)){ + return Result.fail(false, "错误:未获取到当前用户ID"); + } + SsoUser ssoUser = userTempCache.getFromCacheAndDB(userId); + if(StringUtils.isNotEmpty(ssoUser.getGitee())){ + return Result.fail(false, "错误:已绑定gitee账号"); + } + ssoUser.setGitee(giteeAccount); + if(baseMapper.updateById(ssoUser) > 0){ + userTempCache.removeOneCache(AuthInfoUtils.getCurrentUserId()); + return Result.ok(true, "绑定gitee账号成功"); + } + return Result.fail(false, "错误:绑定gitee账号失败"); + } + + @Override + public Result unbindGithub(String userId) { + Result result = unbindVerify(userId); + if(!result.isSuccess()){ + return Result.fail(false, result.getMsg()); + } + SsoUser ssoUser = result.getData(); + if(StringUtils.isEmpty(ssoUser.getGithub())){ + return Result.fail(false, "错误:未绑定github账号"); + } + ssoUser.setGithub(""); + if(baseMapper.updateById(ssoUser) > 0){ + userTempCache.removeOneCache(userId); + return Result.ok(true, "解绑github账号成功"); + } + return Result.fail(false, "解绑github账号失败"); + } + + @Override + public Result bindGithub(String githubAccount) { + SsoUser user = getUserByGithub(githubAccount); + // 新用户判断是否关注仓库 + if (user != null) { + return Result.fail(false, "警告:Github账号已被其他用户绑定"); + } + String userId = AuthInfoUtils.getCurrentUserId(); + if(StringUtils.isEmpty(userId)){ + return Result.fail(false, "错误:未获取到当前用户ID"); + } + SsoUser ssoUser = userTempCache.getFromCacheAndDB(userId); + if(StringUtils.isNotEmpty(ssoUser.getGithub())){ + return Result.fail(false, "错误:已绑定github账号"); + } + ssoUser.setGithub(githubAccount); + if(baseMapper.updateById(ssoUser) > 0){ + userTempCache.removeOneCache(AuthInfoUtils.getCurrentUserId()); + return Result.ok(true, "绑定github账号成功"); + } + return Result.fail(false, "错误:绑定github账号失败"); + } + + private Result unbindVerify(String userId) { + SsoUser ssoUser = userTempCache.getFromCacheAndDB(userId); + if(StringUtils.isEmpty(ssoUser.getPassword())){ + return Result.fail(ssoUser, "错误:未设置密码,请设置密码后再解绑"); + } + if(StringUtils.isMatch("^G[0-9]*$", ssoUser.getAccount())){ + return Result.fail(ssoUser, "错误:自动生成账号,请在个人信息中修改账号名称后再解绑"); + } + return Result.ok(ssoUser, "验证通过"); + } + private OnlineUser buildOnlineUser(RedisAccessToken redisAccessToken, String sessionId) { return new OnlineUser().setAccount(redisAccessToken.getAccount()) .setSid(SM4Utils.encryptEcb(sm4key, sessionId)) diff --git a/mf-start/mf-start-boot/lib/mf-common-bi-mf-2.2.0-pg.jar b/mf-start/mf-start-boot/lib/mf-common-bi-mf-2.2.0-pg.jar index 4d4a58cf..6495988a 100644 Binary files a/mf-start/mf-start-boot/lib/mf-common-bi-mf-2.2.0-pg.jar and b/mf-start/mf-start-boot/lib/mf-common-bi-mf-2.2.0-pg.jar differ diff --git a/mf-start/mf-start-boot/lib/mf-nocode-mf-2.2.0.jar b/mf-start/mf-start-boot/lib/mf-nocode-mf-2.2.0.jar index 186b5956..f5da9f27 100644 Binary files a/mf-start/mf-start-boot/lib/mf-nocode-mf-2.2.0.jar and b/mf-start/mf-start-boot/lib/mf-nocode-mf-2.2.0.jar differ diff --git a/mf-start/mf-start-boot/src/main/resources/application-oauth.yml b/mf-start/mf-start-boot/src/main/resources/application-oauth.yml index ec7e066d..f961ad29 100644 --- a/mf-start/mf-start-boot/src/main/resources/application-oauth.yml +++ b/mf-start/mf-start-boot/src/main/resources/application-oauth.yml @@ -36,7 +36,9 @@ gitee: clientId: a83ea66522fef5f382c6aaffa875c7e87b21d3933277c44a326cd3407fc0b651 clientSecret: 3ebdce2afcb4a7df9b16355f60d898d452f4d9cf8ad1ab8279bb3bb465e614db redirectUri: http://localhost:5186/giteeOauth2 + bindUri: http://localhost:5186/tenant/info/1?callback=gitee github: clientId: Ov23li7oH0Djw3ZEJCoJ clientSecret: ada9a510b3bde542e7510142f07d32a5f6bed131 - redirectUri: http://localhost:5186/githubOauth2 \ No newline at end of file + redirectUri: http://localhost:5186/githubOauth2 + bindUri: http://localhost:5186/tenant/info/1?callback=github \ No newline at end of file diff --git a/mf-start/mf-start-boot/src/main/resources/application-shiro.yml b/mf-start/mf-start-boot/src/main/resources/application-shiro.yml index 24c3171a..7f23406a 100644 --- a/mf-start/mf-start-boot/src/main/resources/application-shiro.yml +++ b/mf-start/mf-start-boot/src/main/resources/application-shiro.yml @@ -9,8 +9,10 @@ security: - "/oauth2/qrCodeLogin/**" - "/oauth2/wx/bind/**" - "/oauth2/static/**" - - "/oauth2/gitee/**" - - "/oauth2/github/**" + - "/oauth2/gitee/token/**" + - "/oauth2/gitee/url" + - "/oauth2/github/token/**" + - "/oauth2/github/url" - "/captcha" - "/storage/file/*.*" - "/css/**" diff --git a/mf-start/mf-start-nocode/lib/mf-common-bi-mf-2.2.0-pg.jar b/mf-start/mf-start-nocode/lib/mf-common-bi-mf-2.2.0-pg.jar index 4d4a58cf..6495988a 100644 Binary files a/mf-start/mf-start-nocode/lib/mf-common-bi-mf-2.2.0-pg.jar and b/mf-start/mf-start-nocode/lib/mf-common-bi-mf-2.2.0-pg.jar differ diff --git a/mf-start/mf-start-nocode/lib/mf-nocode-mf-2.2.0.jar b/mf-start/mf-start-nocode/lib/mf-nocode-mf-2.2.0.jar index 186b5956..f5da9f27 100644 Binary files a/mf-start/mf-start-nocode/lib/mf-nocode-mf-2.2.0.jar and b/mf-start/mf-start-nocode/lib/mf-nocode-mf-2.2.0.jar differ