├── .github └── workflows │ └── docker-publish.yml ├── .gitignore ├── .uplift.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Taskfile.yaml ├── api ├── http │ └── swagger.json └── proto │ ├── gen │ └── resource │ │ └── v1 │ │ └── resource.pb.go │ └── resource │ └── v1 │ └── resource.proto ├── buf.gen.yaml ├── cmd ├── endpoint │ └── endpoint.go ├── initial │ ├── full │ │ ├── full.go │ │ ├── menu │ │ │ └── menu.yaml │ │ └── types.go │ ├── incr │ │ ├── incr-v1.2.3.go │ │ ├── incr-v1.3.0.go │ │ ├── incr-v1.5.0.go │ │ └── types.go │ ├── initial.go │ ├── ioc │ │ ├── app.go │ │ ├── casbin.go │ │ ├── db.go │ │ ├── etcd.go │ │ ├── gin.go │ │ ├── ldap.go │ │ ├── mq.go │ │ ├── mysql.go │ │ ├── redis.go │ │ ├── redisearch.go │ │ ├── wire.go │ │ └── wire_gen.go │ └── version │ │ ├── dao.go │ │ └── service.go ├── root.go └── start │ └── start.go ├── config └── example.yaml ├── deploy ├── Dockerfile ├── docker-compose.yaml ├── install.md ├── install_centos7.sh └── prod.yaml ├── docs └── img │ ├── attribute-secret.png │ ├── cmdb.png │ ├── order.png │ ├── permission.png │ ├── scheduling.png │ └── 自动化任务-设计流程图.png ├── go.mod ├── go.sum ├── init ├── casbin_rule.sql ├── menu.tar.gz └── role.tar.gz ├── internal ├── attribute │ ├── internal │ │ ├── domain │ │ │ └── attribute.go │ │ ├── errs │ │ │ └── code.go │ │ ├── integration │ │ │ ├── handler_test.go │ │ │ └── startup │ │ │ │ ├── db.go │ │ │ │ ├── wire.go │ │ │ │ └── wire_gen.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── attribute.go │ │ │ │ ├── group.go │ │ │ │ └── init.go │ │ │ ├── group.go │ │ │ └── repository.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── mocks │ │ └── attribute.mock.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── codebook │ ├── internal │ │ ├── domain │ │ │ └── codebook.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── codebook.go │ │ │ ├── dao │ │ │ │ └── codebook.go │ │ │ └── error.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── department │ ├── internal │ │ ├── domain │ │ │ └── department.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── department.go │ │ │ └── department.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ ├── tree.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── discovery │ ├── internal │ │ ├── domain │ │ │ └── discovery.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── disvoery.go │ │ │ └── discovery.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── endpoint │ ├── internal │ │ ├── domain │ │ │ └── endpoint.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── endpoint.go │ │ │ │ └── init.go │ │ │ └── endpoint.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── engine │ ├── internal │ │ ├── domain │ │ │ └── instance.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── engine.go │ │ │ └── engine.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── event │ ├── domain │ │ ├── notification.go │ │ └── provider.go │ ├── module.go │ ├── producer │ │ ├── producer.go │ │ └── types.go │ ├── service │ │ ├── channel │ │ │ ├── base.go │ │ │ ├── channel.go │ │ │ └── feishu_card.go │ │ ├── easyflow │ │ │ └── process.go │ │ ├── provider │ │ │ ├── feishu │ │ │ │ └── feishu_card.go │ │ │ ├── sequential │ │ │ │ └── sequential.go │ │ │ └── types.go │ │ ├── sender │ │ │ └── sender.go │ │ └── strategy │ │ │ ├── automation.go │ │ │ ├── base.go │ │ │ ├── result.go │ │ │ ├── start.go │ │ │ ├── types.go │ │ │ ├── user.go │ │ │ └── wrap_send.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── menu │ ├── internal │ │ ├── domain │ │ │ └── menu.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── producer.go │ │ │ └── types.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── menu.go │ │ │ └── menu.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ ├── tree.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── model │ ├── internal │ │ ├── domain │ │ │ └── model.go │ │ ├── errs │ │ │ └── code.go │ │ ├── integration │ │ │ ├── handler_test.go │ │ │ └── startup │ │ │ │ ├── wire.go │ │ │ │ └── wire_gen.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── group.go │ │ │ │ ├── model.go │ │ │ │ └── type.go │ │ │ ├── group.go │ │ │ └── model.go │ │ ├── service │ │ │ ├── group.go │ │ │ └── model.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ ├── retrieve.go │ │ │ └── vo.go │ ├── module.go │ ├── type.go │ ├── wire.go │ └── wire_gen.go ├── order │ ├── internal │ │ ├── domain │ │ │ └── order.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer │ │ │ │ ├── feishu_callback.go │ │ │ │ ├── modify_status.go │ │ │ │ ├── process.go │ │ │ │ └── wechat.go │ │ │ ├── producer.go │ │ │ └── types.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── order.go │ │ │ └── order.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── permission │ ├── REDEME.md │ ├── internal │ │ ├── domain │ │ │ └── permission.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer.go │ │ │ └── types.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── permission.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── model.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── pkg │ ├── middleware │ │ └── check_policy_builder.go │ ├── rule │ │ ├── field.go │ │ ├── rule.go │ │ ├── rule.json │ │ ├── rule_test.go │ │ └── title.go │ └── wechat │ │ └── marshal.go ├── policy │ ├── internal │ │ ├── domain │ │ │ └── policy.go │ │ ├── errs │ │ │ └── code.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── relation │ ├── REDEME.md │ ├── internal │ │ ├── domain │ │ │ ├── realtion_resource.go │ │ │ └── relation.go │ │ ├── errs │ │ │ └── code.go │ │ ├── integration │ │ │ ├── handler_rm_test.go │ │ │ ├── handler_rr_test.go │ │ │ ├── service_rm_test.go │ │ │ ├── service_rr_test.go │ │ │ └── startup │ │ │ │ ├── db.go │ │ │ │ ├── wire.go │ │ │ │ └── wire_gen.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── relation_model.go │ │ │ │ ├── relation_resource.go │ │ │ │ ├── relation_type.go │ │ │ │ └── type.go │ │ │ ├── relation_model.go │ │ │ ├── relation_resource.go │ │ │ └── relation_type.go │ │ ├── service │ │ │ ├── relation_model.go │ │ │ ├── relation_resource.go │ │ │ └── relation_type.go │ │ └── web │ │ │ ├── relation_model_handler.go │ │ │ ├── relation_resource_handler.go │ │ │ ├── relation_type_handler.go │ │ │ ├── result.go │ │ │ ├── type.go │ │ │ └── vo.go │ ├── mocks │ │ ├── relation_model.mock.go │ │ └── relation_resource.mock.go │ ├── module.go │ ├── type.go │ ├── wire.go │ └── wire_gen.go ├── resource │ ├── internal │ │ ├── domain │ │ │ └── resource.go │ │ ├── errs │ │ │ └── code.go │ │ ├── integration │ │ │ ├── handler_test.go │ │ │ └── startup │ │ │ │ ├── db.go │ │ │ │ ├── wire.go │ │ │ │ └── wire_gen.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── init.go │ │ │ │ └── resource.go │ │ │ └── repository.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── mocks │ │ └── resource.mock.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── role │ ├── internal │ │ ├── domain │ │ │ └── role.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── init.go │ │ │ │ └── role.go │ │ │ └── role.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── rota │ ├── README.md │ ├── internal │ │ ├── domain │ │ │ └── rota.go │ │ ├── errs │ │ │ └── code.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── rota.go │ │ │ └── rota.go │ │ ├── service │ │ │ ├── schedule │ │ │ │ ├── rrule.go │ │ │ │ └── types.go │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── rrule_test.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── runner │ ├── internal │ │ ├── domain │ │ │ └── runner.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer.go │ │ │ └── types.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── runner.go │ │ │ ├── error.go │ │ │ └── runner.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── strategy │ ├── internal │ │ ├── domain │ │ │ └── strategy.go │ │ ├── errs │ │ │ └── code.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── task │ ├── README.md │ ├── internal │ │ ├── domain │ │ │ └── task.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer.go │ │ │ └── types.go │ │ ├── job │ │ │ ├── offine_recovery.go │ │ │ ├── pass_process_task_job.go │ │ │ └── start_task_job.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── task.go │ │ │ └── task.go │ │ ├── service │ │ │ ├── cronjob.go │ │ │ ├── exec.go │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── template │ ├── internal │ │ ├── domain │ │ │ ├── domain.go │ │ │ ├── group.go │ │ │ └── wechat.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer.go │ │ │ ├── producer.go │ │ │ └── types.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ ├── group.go │ │ │ │ ├── template.go │ │ │ │ └── types.go │ │ │ ├── error.go │ │ │ ├── group.go │ │ │ └── template.go │ │ ├── service │ │ │ ├── group.go │ │ │ └── service.go │ │ └── web │ │ │ ├── group.go │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── terminal │ ├── internal │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── session.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── test │ └── ioc │ │ └── db.go ├── tools │ ├── errs │ │ └── code.go │ ├── service │ │ └── service.go │ ├── types.go │ ├── web │ │ ├── handler.go │ │ ├── result.go │ │ └── vo.go │ ├── wire.go │ └── wire_gen.go ├── user │ ├── REAEME.md │ ├── internal │ │ ├── domain │ │ │ ├── feishu.go │ │ │ ├── ldap.go │ │ │ ├── user.go │ │ │ └── wechat.go │ │ ├── errs │ │ │ └── code.go │ │ ├── job │ │ │ └── sync.go │ │ ├── repository │ │ │ ├── cache │ │ │ │ ├── redis_ldap.go │ │ │ │ └── redisearch_ldap.go │ │ │ ├── dao │ │ │ │ └── user.go │ │ │ └── user.go │ │ ├── service │ │ │ ├── ldap.go │ │ │ ├── passkey.go │ │ │ └── user.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── ldap.go │ │ │ ├── result.go │ │ │ ├── tree.go │ │ │ └── vo.go │ ├── ldapx │ │ ├── conn.go │ │ ├── ldap.go │ │ └── type.go │ ├── module.go │ ├── passkey │ │ └── passkey.go │ ├── type.go │ ├── wire.go │ └── wire_gen.go ├── worker │ ├── internal │ │ ├── domain │ │ │ └── worker.go │ │ ├── errs │ │ │ └── code.go │ │ ├── event │ │ │ ├── consumer.go │ │ │ ├── producer.go │ │ │ ├── types.go │ │ │ └── watch │ │ │ │ └── watch.go │ │ ├── repository │ │ │ ├── dao │ │ │ │ └── worker.go │ │ │ ├── error.go │ │ │ └── worker.go │ │ ├── service │ │ │ └── service.go │ │ └── web │ │ │ ├── handler.go │ │ │ ├── result.go │ │ │ └── vo.go │ ├── module.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go └── workflow │ ├── internal │ ├── domain │ │ ├── logicflow.go │ │ └── workflow.go │ ├── errs │ │ └── code.go │ ├── repository │ │ ├── dao │ │ │ └── workflow.go │ │ └── workflow.go │ ├── service │ │ └── service.go │ └── web │ │ ├── handler.go │ │ ├── result.go │ │ └── vo.go │ ├── module.go │ ├── pkg │ └── easyflow │ │ ├── convert.go │ │ └── types.go │ ├── types.go │ ├── wire.go │ └── wire_gen.go ├── ioc ├── app.go ├── casbin.go ├── db.go ├── etcd.go ├── feishu.go ├── gin.go ├── job.go ├── kafka │ ├── addr.go │ ├── consumer_test.go │ └── producer_test.go ├── ldap.go ├── minio.go ├── mq.go ├── mysql.go ├── redis.go ├── redisearch.go ├── session.go ├── wire.go ├── wire_gen.go └── workwx.go ├── main.go └── pkg ├── cryptox ├── aes.go └── aes_test.go ├── genrate_test.go ├── ginx ├── test │ ├── recorder.go │ └── result.go ├── type.go └── wrap.go ├── hash ├── hash.go └── hash_test.go ├── mongox ├── collection.go ├── filter.go ├── mongo.go ├── sql.db ├── tx.go └── type.go ├── mqx ├── general_producer.go └── multiple_producer.go ├── registry ├── etcd │ └── registry.go └── types.go ├── term ├── guacx │ ├── config.go │ ├── guacd.go │ ├── handler.go │ └── instruction.go ├── sessions.go └── sshx │ ├── client.go │ ├── connet.go │ ├── gatewhy.go │ └── message.go └── tools └── slice.go /.gitignore: -------------------------------------------------------------------------------- 1 | config/prod.toml 2 | config/prod.yaml 3 | logs/ 4 | .idea/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 duke1616 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Taskfile.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | tasks: 3 | default: 4 | desc: 命令提示 5 | cmds: 6 | - task --list-all 7 | init: 8 | desc: 初始化系统 9 | cmds: 10 | - EGO_DEBUG=true go run main.go init 11 | run: 12 | desc: 启动服务 13 | cmds: 14 | - EGO_DEBUG=true go run main.go start 15 | gen: 16 | desc: 代码生成 17 | cmds: 18 | - buf generate api/proto 19 | 20 | 21 | -------------------------------------------------------------------------------- /api/http/swagger.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/api/http/swagger.json -------------------------------------------------------------------------------- /api/proto/resource/v1/resource.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package resource.v1; 4 | option go_package="resource/v1;resource"; 5 | import "google/protobuf/timestamp.proto"; // 使用timestamp类型 6 | 7 | message Resource { 8 | int64 id = 1; 9 | string name = 2; 10 | Type type = 3; 11 | google.protobuf.Timestamp ctime = 4; 12 | } 13 | 14 | enum Type { 15 | Unknown = 0; 16 | Host = 1; 17 | Firewalld = 2; 18 | } -------------------------------------------------------------------------------- /buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: github.com/Duke1616/ecmdb/api/proto/gen 6 | plugins: 7 | # 默认用最新版本 8 | - plugin: buf.build/protocolbuffers/go 9 | # 指定版本 buf.build/protocolbuffers/go:v1.28.1 10 | # protoc-gen-go 插件 11 | out: api/proto/gen 12 | opt: paths=source_relative 13 | 14 | - plugin: buf.build/grpc/go 15 | # protoc-gen-go-grpc 插件 16 | out: api/proto/gen 17 | opt: 18 | - paths=source_relative -------------------------------------------------------------------------------- /cmd/endpoint/endpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/internal/endpoint" 7 | "github.com/Duke1616/ecmdb/ioc" 8 | "github.com/gin-gonic/gin" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var Cmd = &cobra.Command{ 13 | Use: "endpoint", 14 | Short: "ecmdb endpoint", 15 | Long: "注册所有路由信息到 Endpoint 中,用于动态菜单API鉴权中使用", 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | app, err := ioc.InitApp() 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | err = initEndpoint(app.Web, app.Svc) 23 | fmt.Print(err) 24 | panic(err) 25 | }, 26 | } 27 | 28 | // 生成端点路由信息、方便菜单权限绑定路由 29 | func initEndpoint(web *gin.Engine, svc endpoint.Service) error { 30 | routes := web.Routes() 31 | for _, route := range routes { 32 | _, err := svc.RegisterEndpoint(context.Background(), endpoint.Endpoint{ 33 | Method: route.Method, 34 | Path: route.Path, 35 | }) 36 | 37 | if err != nil { 38 | return err 39 | } 40 | } 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /cmd/initial/full/full.go: -------------------------------------------------------------------------------- 1 | package full 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/Duke1616/ecmdb/internal/role" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "time" 9 | ) 10 | 11 | func (i *fullInitial) InitUser() error { 12 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 13 | defer cancel() 14 | 15 | // 查找用户是否存在 16 | _, err := i.App.UserSvc.FindById(ctx, 1) 17 | if !errors.Is(err, mongo.ErrNoDocuments) { 18 | return nil 19 | } 20 | 21 | if err != nil { 22 | return err 23 | } 24 | 25 | // 创建用户 26 | _, err = i.App.UserSvc.FindOrCreateBySystem(ctx, UserName, Password, DisPlayName) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return nil 32 | } 33 | 34 | func (i *fullInitial) InitRole() error { 35 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 36 | defer cancel() 37 | 38 | // 查找用户是否存在 39 | _, err := i.App.RoleSvc.FindByRoleCode(ctx, RoleCode) 40 | if !errors.Is(err, mongo.ErrNoDocuments) { 41 | return nil 42 | } 43 | 44 | if err != nil { 45 | return err 46 | } 47 | 48 | // 创建用户 49 | _, err = i.App.RoleSvc.CreateRole(ctx, role.Role{ 50 | Name: "超级管理员", 51 | Code: RoleCode, 52 | Status: true, 53 | }) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func (i *fullInitial) InitMenu() error { 62 | _, cancel := context.WithTimeout(context.Background(), time.Second*3) 63 | defer cancel() 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /cmd/initial/full/menu/menu.yaml: -------------------------------------------------------------------------------- 1 | name: 模型管理 2 | role: 3 | children: 4 | - name: 模型资产 5 | endpoints: 6 | - method: 7 | path: 8 | desc: 9 | - method: 10 | path: 11 | desc: 12 | - name: 模型列表 13 | endpoints: 14 | - method: 15 | path: 16 | desc: 17 | - method: 18 | path: 19 | desc: 20 | - name: 关联关系 21 | endpoints: 22 | - method: 23 | path: 24 | desc: 25 | - method: 26 | path: 27 | desc: 28 | - name: 关联类型 29 | 30 | 31 | -------------------------------------------------------------------------------- /cmd/initial/full/types.go: -------------------------------------------------------------------------------- 1 | package full 2 | 3 | import "github.com/Duke1616/ecmdb/cmd/initial/ioc" 4 | 5 | // 用户相关 6 | const ( 7 | UserName = "admin" 8 | Password = "123456" 9 | DisPlayName = "超级管理员" 10 | ) 11 | 12 | // 角色相关 13 | const ( 14 | RoleCode = "admin" 15 | Desc = "" 16 | ) 17 | 18 | type InitialFull interface { 19 | InitUser() error 20 | InitRole() error 21 | InitMenu() error 22 | } 23 | 24 | type fullInitial struct { 25 | App *ioc.App 26 | } 27 | 28 | func NewInitial(app *ioc.App) InitialFull { 29 | return &fullInitial{ 30 | App: app, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cmd/initial/incr/incr-v1.2.3.go: -------------------------------------------------------------------------------- 1 | package incr 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/cmd/initial/ioc" 6 | ) 7 | 8 | type incrV123 struct { 9 | App *ioc.App 10 | } 11 | 12 | func NewIncrV123(app *ioc.App) InitialIncr { 13 | return &incrV123{ 14 | App: app, 15 | } 16 | } 17 | 18 | func (i *incrV123) Version() string { 19 | return "v1.2.3" 20 | } 21 | 22 | func (i *incrV123) Commit() error { 23 | return nil 24 | } 25 | 26 | func (i *incrV123) Rollback() error { 27 | return nil 28 | } 29 | 30 | func (i *incrV123) After() error { 31 | return nil 32 | } 33 | 34 | func (i *incrV123) Before() error { 35 | return i.App.VerSvc.CreateOrUpdateVersion(context.Background(), i.Version()) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/initial/incr/incr-v1.3.0.go: -------------------------------------------------------------------------------- 1 | package incr 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/cmd/initial/ioc" 6 | ) 7 | 8 | type incrV130 struct { 9 | App *ioc.App 10 | } 11 | 12 | func NewIncrV130(app *ioc.App) InitialIncr { 13 | return &incrV130{ 14 | App: app, 15 | } 16 | } 17 | 18 | func (i *incrV130) Version() string { 19 | return "v1.3.0" 20 | } 21 | 22 | func (i *incrV130) Commit() error { 23 | return nil 24 | } 25 | 26 | func (i *incrV130) Rollback() error { 27 | return nil 28 | } 29 | 30 | func (i *incrV130) After() error { 31 | return nil 32 | } 33 | 34 | func (i *incrV130) Before() error { 35 | return i.App.VerSvc.CreateOrUpdateVersion(context.Background(), i.Version()) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/initial/incr/incr-v1.5.0.go: -------------------------------------------------------------------------------- 1 | package incr 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/cmd/initial/ioc" 6 | ) 7 | 8 | type incrV150 struct { 9 | App *ioc.App 10 | } 11 | 12 | func NewIncrV150(app *ioc.App) InitialIncr { 13 | return &incrV150{ 14 | App: app, 15 | } 16 | } 17 | 18 | func (i *incrV150) Version() string { 19 | return "v1.5.0" 20 | } 21 | 22 | func (i *incrV150) Commit() error { 23 | return nil 24 | } 25 | 26 | func (i *incrV150) Rollback() error { 27 | return nil 28 | } 29 | 30 | func (i *incrV150) After() error { 31 | return nil 32 | } 33 | 34 | func (i *incrV150) Before() error { 35 | return i.App.VerSvc.CreateOrUpdateVersion(context.Background(), i.Version()) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/initial/initial.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/cmd/initial/full" 6 | "github.com/Duke1616/ecmdb/cmd/initial/incr" 7 | "github.com/Duke1616/ecmdb/cmd/initial/ioc" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var ( 12 | debug bool 13 | TagVersion string 14 | ) 15 | 16 | var Cmd = &cobra.Command{ 17 | Use: "init", 18 | Short: "初始化应用服务", 19 | Long: "初始化应用服务,作为环境演示", 20 | Run: func(cmd *cobra.Command, args []string) { 21 | // 初始化 Ioc 注册 22 | app, err := ioc.InitApp() 23 | cobra.CheckErr(err) 24 | 25 | // 获取系统版本信息 26 | currentVersion, err := app.VerSvc.GetVersion(context.Background()) 27 | cobra.CheckErr(err) 28 | 29 | // 判断是执行全量 OR 增量数据 30 | if currentVersion == "" { 31 | complete(app) 32 | increment(app, "v1.0.0") 33 | } else { 34 | increment(app, currentVersion) 35 | } 36 | }, 37 | } 38 | 39 | func complete(app *ioc.App) { 40 | // 初始化Init 41 | init := full.NewInitial(app) 42 | 43 | // 初始化菜单 44 | err := init.InitMenu() 45 | cobra.CheckErr(err) 46 | 47 | // 初始化用户 48 | err = init.InitUser() 49 | cobra.CheckErr(err) 50 | 51 | // 初始化角色 52 | err = init.InitRole() 53 | cobra.CheckErr(err) 54 | } 55 | 56 | func increment(app *ioc.App, currentVersion string) { 57 | // 注册所有增量版本信息 58 | incr.RegisterIncr(app) 59 | 60 | // 执行增量数据 61 | err := incr.RunIncrementalOperations(currentVersion) 62 | cobra.CheckErr(err) 63 | } 64 | 65 | func init() { 66 | Cmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "show debug info") 67 | } 68 | -------------------------------------------------------------------------------- /cmd/initial/ioc/app.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/cmd/initial/version" 5 | "github.com/Duke1616/ecmdb/internal/role" 6 | "github.com/Duke1616/ecmdb/internal/user" 7 | ) 8 | 9 | type App struct { 10 | UserSvc user.Service 11 | RoleSvc role.Service 12 | VerSvc version.Service 13 | } 14 | -------------------------------------------------------------------------------- /cmd/initial/ioc/db.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/pkg/mongox" 7 | "github.com/spf13/viper" 8 | "go.mongodb.org/mongo-driver/event" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | "log" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func InitMongoDB() *mongox.Mongo { 17 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 18 | defer cancel() 19 | 20 | monitor := &event.CommandMonitor{ 21 | Started: func(ctx context.Context, evt *event.CommandStartedEvent) { 22 | //fmt.Println(evt.Command) 23 | }, 24 | } 25 | 26 | type Config struct { 27 | DSN string `mapstructure:"dsn"` 28 | Username string `mapstructure:"username"` 29 | Password string `mapstructure:"password"` 30 | } 31 | 32 | var cfg Config 33 | if err := viper.UnmarshalKey("mongodb", &cfg); err != nil { 34 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 35 | } 36 | 37 | dns := strings.Split(cfg.DSN, "//") 38 | uri := fmt.Sprintf("%s//%s:%s@%s", dns[0], cfg.Username, cfg.Password, dns[1]) 39 | 40 | opts := options.Client(). 41 | ApplyURI(uri). 42 | SetMonitor(monitor) 43 | client, err := mongo.Connect(ctx, opts) 44 | 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | if err = client.Ping(ctx, nil); err != nil { 50 | log.Panicf("ping mongodb server error, %s", err) 51 | } 52 | 53 | return mongox.NewMongo(client, "cmdb") 54 | } 55 | -------------------------------------------------------------------------------- /cmd/initial/ioc/etcd.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/viper" 6 | clientv3 "go.etcd.io/etcd/client/v3" 7 | ) 8 | 9 | func InitEtcdClient() *clientv3.Client { 10 | var cfg clientv3.Config 11 | 12 | if err := viper.UnmarshalKey("etcd", &cfg); err != nil { 13 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 14 | } 15 | 16 | client, err := clientv3.New(cfg) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | return client 22 | } 23 | -------------------------------------------------------------------------------- /cmd/initial/ioc/ldap.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Duke1616/ecmdb/internal/user/ldapx" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func InitLdapConfig() ldapx.Config { 10 | // 定义一个结构体实例 11 | var cfg ldapx.Config 12 | 13 | // 使用 Unmarshal 函数将配置数据解析到结构体中 14 | if err := viper.UnmarshalKey("ldap", &cfg); err != nil { 15 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 16 | } 17 | 18 | return cfg 19 | } 20 | -------------------------------------------------------------------------------- /cmd/initial/ioc/mq.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ecodeclub/ekit/retry" 6 | "github.com/ecodeclub/mq-api" 7 | "github.com/ecodeclub/mq-api/kafka" 8 | "github.com/spf13/viper" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | q mq.MQ 15 | mqInitOnce sync.Once 16 | ) 17 | 18 | func InitMQ() mq.MQ { 19 | mqInitOnce.Do(func() { 20 | const maxInterval = 10 * time.Second 21 | const maxRetries = 10 22 | strategy, err := retry.NewExponentialBackoffRetryStrategy(time.Second, maxInterval, maxRetries) 23 | if err != nil { 24 | panic(err) 25 | } 26 | for { 27 | q, err = initMQ() 28 | if err == nil { 29 | break 30 | } 31 | next, ok := strategy.Next() 32 | if !ok { 33 | panic("InitMQ 重试失败......") 34 | } 35 | time.Sleep(next) 36 | } 37 | }) 38 | return q 39 | } 40 | 41 | func initMQ() (mq.MQ, error) { 42 | type Config struct { 43 | Network string `yaml:"network"` 44 | Addresses []string `yaml:"addresses"` 45 | } 46 | 47 | var cfg Config 48 | if err := viper.UnmarshalKey("kafka", &cfg); err != nil { 49 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 50 | } 51 | 52 | qq, err := kafka.NewMQ(cfg.Network, cfg.Addresses) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return qq, nil 57 | } 58 | -------------------------------------------------------------------------------- /cmd/initial/ioc/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ecodeclub 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ioc 16 | 17 | import ( 18 | "fmt" 19 | "github.com/redis/go-redis/v9" 20 | "github.com/spf13/viper" 21 | ) 22 | 23 | func InitRedis() redis.Cmdable { 24 | type Config struct { 25 | Addr string `mapstructure:"addr"` 26 | Password string `mapstructure:"password"` 27 | DB int `mapstructure:"db"` 28 | } 29 | 30 | var cfg Config 31 | 32 | if err := viper.UnmarshalKey("redis", &cfg); err != nil { 33 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 34 | } 35 | 36 | cmd := redis.NewClient(&redis.Options{ 37 | Addr: cfg.Addr, 38 | Password: cfg.Password, 39 | DB: cfg.DB, 40 | }) 41 | return cmd 42 | } 43 | -------------------------------------------------------------------------------- /cmd/initial/ioc/redisearch.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RediSearch/redisearch-go/v2/redisearch" 6 | "github.com/gomodule/redigo/redis" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func InitRediSearch() *redisearch.Client { 11 | type Config struct { 12 | Addr string `mapstructure:"addr"` 13 | Password string `mapstructure:"password"` 14 | DB int `mapstructure:"db"` 15 | } 16 | 17 | var cfg Config 18 | if err := viper.UnmarshalKey("redis", &cfg); err != nil { 19 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 20 | } 21 | 22 | pool := &redis.Pool{Dial: func() (redis.Conn, error) { 23 | return redis.Dial("tcp", cfg.Addr, 24 | redis.DialPassword(cfg.Password), 25 | redis.DialDatabase(cfg.DB)) 26 | }} 27 | 28 | return redisearch.NewClientFromPool(pool, "index") 29 | } 30 | -------------------------------------------------------------------------------- /cmd/initial/ioc/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package ioc 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/cmd/initial/version" 7 | "github.com/Duke1616/ecmdb/internal/department" 8 | "github.com/Duke1616/ecmdb/internal/policy" 9 | "github.com/Duke1616/ecmdb/internal/role" 10 | "github.com/Duke1616/ecmdb/internal/user" 11 | "github.com/google/wire" 12 | ) 13 | 14 | var BaseSet = wire.NewSet(InitMongoDB, InitMySQLDB, InitRedis, InitRediSearch, InitMQ, InitEtcdClient, InitLdapConfig) 15 | 16 | func InitApp() (*App, error) { 17 | wire.Build(wire.Struct(new(App), "*"), 18 | BaseSet, 19 | InitCasbin, 20 | user.InitModule, 21 | version.NewService, 22 | version.NewDao, 23 | wire.FieldsOf(new(*user.Module), "Svc"), 24 | department.InitModule, 25 | role.InitModule, 26 | wire.FieldsOf(new(*role.Module), "Svc"), 27 | policy.InitModule, 28 | ) 29 | return new(App), nil 30 | } 31 | -------------------------------------------------------------------------------- /cmd/initial/version/dao.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/pkg/mongox" 7 | "go.mongodb.org/mongo-driver/bson" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | "time" 10 | ) 11 | 12 | type Dao interface { 13 | CreateOrUpdateVersion(ctx context.Context, version string) error 14 | GetVersion(ctx context.Context) (string, error) 15 | } 16 | 17 | type dao struct { 18 | db *mongox.Mongo 19 | } 20 | 21 | func (d *dao) GetVersion(ctx context.Context) (string, error) { 22 | col := d.db.Collection("c_version") 23 | var result Version 24 | filter := bson.M{} 25 | 26 | if err := col.FindOne(ctx, filter).Decode(&result); err != nil { 27 | return "", fmt.Errorf("解码错误,%w", err) 28 | } 29 | 30 | return result.CurrentVersion, nil 31 | } 32 | 33 | func NewDao(db *mongox.Mongo) Dao { 34 | return &dao{ 35 | db: db, 36 | } 37 | } 38 | 39 | func (d *dao) CreateOrUpdateVersion(ctx context.Context, version string) error { 40 | col := d.db.Collection("c_version") 41 | filter := bson.M{} 42 | updateDoc := bson.M{ 43 | "$set": bson.M{ 44 | "current_version": version, 45 | "utime": time.Now().UnixMilli(), 46 | }, 47 | } 48 | 49 | upsert := true 50 | opts := &options.UpdateOptions{ 51 | Upsert: &upsert, 52 | } 53 | 54 | _, err := col.UpdateOne(ctx, filter, updateDoc, opts) 55 | return err 56 | } 57 | 58 | type Version struct { 59 | // 当前服务初始化数据版本 60 | CurrentVersion string `json:"current_version" bson:"current_version"` 61 | Ctime int64 `bson:"ctime"` 62 | Utime int64 `bson:"utime"` 63 | } 64 | -------------------------------------------------------------------------------- /cmd/initial/version/service.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | ) 8 | 9 | type Service interface { 10 | CreateOrUpdateVersion(ctx context.Context, version string) error 11 | GetVersion(ctx context.Context) (string, error) 12 | } 13 | 14 | type service struct { 15 | dao Dao 16 | } 17 | 18 | func (s service) GetVersion(ctx context.Context) (string, error) { 19 | ver, err := s.dao.GetVersion(ctx) 20 | if errors.Is(err, mongo.ErrNoDocuments) { 21 | return "", nil 22 | } 23 | 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | return ver, nil 29 | } 30 | 31 | func (s service) CreateOrUpdateVersion(ctx context.Context, version string) error { 32 | return s.dao.CreateOrUpdateVersion(ctx, version) 33 | } 34 | 35 | func NewService(dao Dao) Service { 36 | return service{ 37 | dao: dao, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/cmd/endpoint" 5 | "github.com/Duke1616/ecmdb/cmd/initial" 6 | "github.com/Duke1616/ecmdb/cmd/start" 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/pflag" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | var ( 13 | confFile string 14 | ) 15 | 16 | var rootCmd = &cobra.Command{ 17 | Use: "ecmdb", 18 | Short: "CMDB、工单一体化平台", 19 | Long: "CMDB、工单一体化平台", 20 | RunE: func(cmd *cobra.Command, args []string) error { 21 | return cmd.Help() 22 | }, 23 | } 24 | 25 | func initAll() { 26 | // 初始化配置文件 27 | initViper() 28 | } 29 | 30 | func initViper() { 31 | file := pflag.String("config", 32 | confFile, "配置文件路径") 33 | pflag.Parse() 34 | // 直接指定文件路径 35 | viper.SetConfigFile(*file) 36 | viper.WatchConfig() 37 | err := viper.ReadInConfig() 38 | 39 | if err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | func Execute(version string) { 45 | // 版本初始化 46 | initial.TagVersion = version 47 | 48 | // 初始化设置 49 | cobra.OnInitialize(initAll) 50 | rootCmd.AddCommand(start.Cmd) 51 | rootCmd.AddCommand(initial.Cmd) 52 | rootCmd.AddCommand(endpoint.Cmd) 53 | err := rootCmd.Execute() 54 | cobra.CheckErr(err) 55 | } 56 | 57 | func init() { 58 | rootCmd.PersistentFlags().StringVarP(&confFile, "config-file", "f", "config/prod.yaml", "the service config from file") 59 | } 60 | -------------------------------------------------------------------------------- /cmd/start/start.go: -------------------------------------------------------------------------------- 1 | package start 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Bunny3th/easy-workflow/workflow/engine" 6 | "github.com/Duke1616/ecmdb/ioc" 7 | "github.com/gotomicro/ego/task/ecron" 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | var Cmd = &cobra.Command{ 13 | Use: "start", 14 | Short: "ecmdb API服务", 15 | Long: "启动服务,对外暴露接口", 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | app, err := ioc.InitApp() 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | initCronjob(app.Jobs) 23 | engine.RegisterEvents(app.Event) 24 | 25 | err = app.Web.Run(":8000") 26 | panic(err) 27 | }, 28 | } 29 | 30 | // 注册定时任务 31 | func initCronjob(jobs []*ecron.Component) { 32 | type Config struct { 33 | Enabled bool `mapstructure:"enabled"` 34 | } 35 | 36 | var cfg Config 37 | if err := viper.UnmarshalKey("cronjob", &cfg); err != nil { 38 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 39 | } 40 | 41 | if !cfg.Enabled { 42 | return 43 | } 44 | 45 | for _, job := range jobs { 46 | go job.Start() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /config/example.yaml: -------------------------------------------------------------------------------- 1 | crypto_aes_key: "1234567890" 2 | 3 | ldap: 4 | url: "ldap://127.0.0.1:389" 5 | base_dn: "DC=example,DC=com" 6 | bind_dn: "CN=cn,OU=ou,DC=example,DC=com" 7 | bind_password: "1234567890" 8 | username_attribute: "sAMAccountName" 9 | mail_attribute: "mail" 10 | display_name_attribute: "displayName" 11 | title_attribute: "title" 12 | group_name_attribute: "cn" 13 | user_filter: "(&(sAMAccountName={input})(!(UserAccountControl=514)))" 14 | group_filter: "(|(member={dn}))" 15 | sync_user_filter: "(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=512)(userAccountControl:1.2.840.113556.1.4.803:=512)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" 16 | sync_exclude_ou: "unknown" 17 | 18 | session: 19 | session_encrypted_key: "1234567890" 20 | 21 | redis: 22 | addr: "127.0.0.1:6379" 23 | password: "1234567890" 24 | db: 0 25 | 26 | mongodb: 27 | dsn: mongodb://127.0.0.1:27017/cmdb 28 | username: cmdb 29 | password: 123456 30 | 31 | kafka: 32 | network: tcp 33 | addresses: 34 | - 127.0.0.1:9092 35 | 36 | etcd: 37 | endpoints: 38 | - 127.0.0.1:2379 39 | 40 | wechat: 41 | corpId: "" 42 | corpSecret: "" 43 | agentId: 1000002 44 | 45 | cronjob: 46 | enabled: true -------------------------------------------------------------------------------- /deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24.1-alpine as build 2 | MAINTAINER luankz 3 | WORKDIR /app 4 | COPY . . 5 | # ENV GOPROXY="https://goproxy.cn,direct" 6 | # ENV GOOS=linux GOARCH=amd64 CGO_ENABLED=0 7 | RUN go mod tidy 8 | ARG VERSION=unknown 9 | RUN go build -ldflags="-X main.version=${VERSION} -w -s" -a -o dist/ecmdb main.go 10 | FROM chromedp/headless-shell:133.0.6905.0 as deploy 11 | RUN apt-get update && apt-get install -y ca-certificates fonts-wqy-microhei && update-ca-certificates 12 | ENV EGO_DEBUG=true 13 | ENV PATH /headless-shell:$PATH 14 | WORKDIR /app 15 | COPY --from=build /app/dist/ecmdb . 16 | EXPOSE 8000 17 | ENTRYPOINT ["./ecmdb", "start"] -------------------------------------------------------------------------------- /deploy/prod.yaml: -------------------------------------------------------------------------------- 1 | crypto_aes_key: "1234567890" 2 | 3 | ldap: 4 | url: "ldap://127.0.0.1:389" 5 | base_dn: "DC=example,DC=com" 6 | bind_dn: "CN=cn,OU=ou,DC=example,DC=com" 7 | bind_password: "1234567890" 8 | username_attribute: "sAMAccountName" 9 | mail_attribute: "mail" 10 | display_name_attribute: "displayName" 11 | title_attribute: "title" 12 | group_name_attribute: "cn" 13 | user_filter: "(&(sAMAccountName={input})(!(UserAccountControl=514)))" 14 | group_filter: "(|(member={dn}))" 15 | 16 | session: 17 | session_encrypted_key: "1234567890" 18 | 19 | redis: 20 | addr: "ecmdb-redis:6379" 21 | password: "1234567890" 22 | db: 0 23 | 24 | casbin: 25 | redis: 26 | addr: "ecmdb-redis:6379" 27 | db: 1 28 | password: "1234567890" 29 | 30 | mongodb: 31 | dsn: mongodb://mongo:27017/ecmdb?authSource=admin 32 | db: ecmdb 33 | username: ecmdb 34 | password: 123456 35 | 36 | mysql: 37 | dsn: ecmdb:123456@tcp(ecmdb-mysql:3306)/ecmdb?charset=utf8mb4&parseTime=True&loc=Local&timeout=5s 38 | 39 | kafka: 40 | network: tcp 41 | addresses: 42 | - ecmdb-kafka:9092 43 | 44 | etcd: 45 | endpoints: 46 | - ecmdb-etcd:2379 47 | 48 | minio: 49 | endpoint: minio:9000 50 | accessKeyID: minio 51 | secretAccessKey: minio123 52 | useSSL: false 53 | 54 | # 用于查询企业微信API 55 | wechat: 56 | corpId: "" 57 | corpSecret: "" 58 | agentId: 1000002 59 | 60 | # 用于飞书发送消息通知 61 | feishu: 62 | appId: "" 63 | appSecret: "" 64 | 65 | frontend: 66 | logicflow_url: http://localhost:3333/logicflow-preview 67 | 68 | cronjob: 69 | enabled: false 70 | -------------------------------------------------------------------------------- /docs/img/attribute-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/attribute-secret.png -------------------------------------------------------------------------------- /docs/img/cmdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/cmdb.png -------------------------------------------------------------------------------- /docs/img/order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/order.png -------------------------------------------------------------------------------- /docs/img/permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/permission.png -------------------------------------------------------------------------------- /docs/img/scheduling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/scheduling.png -------------------------------------------------------------------------------- /docs/img/自动化任务-设计流程图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/docs/img/自动化任务-设计流程图.png -------------------------------------------------------------------------------- /init/menu.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/init/menu.tar.gz -------------------------------------------------------------------------------- /init/role.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duke1616/ecmdb/f19991d44218e5a0042348369428a9b5e236c3ef/init/role.tar.gz -------------------------------------------------------------------------------- /internal/attribute/internal/domain/attribute.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Attribute struct { 4 | ID int64 5 | GroupId int64 6 | ModelUid string 7 | FieldUid string 8 | FieldName string 9 | FieldType string 10 | Required bool 11 | Display bool 12 | Secure bool 13 | Link bool 14 | Index int64 15 | Option interface{} 16 | } 17 | 18 | type AttributeGroup struct { 19 | ID int64 20 | Name string 21 | ModelUid string 22 | Index int64 23 | } 24 | 25 | type AttributePipeline struct { 26 | GroupId int64 `bson:"_id"` 27 | Total int `bson:"total"` 28 | Attributes []Attribute `bson:"attributes"` 29 | } 30 | -------------------------------------------------------------------------------- /internal/attribute/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 502001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/attribute/internal/integration/startup/db.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | "log" 9 | "time" 10 | ) 11 | 12 | func InitMongoDB() *mongox.Mongo { 13 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 14 | defer cancel() 15 | 16 | opts := options.Client(). 17 | ApplyURI("mongodb://cmdb:123456@10.31.0.200:47017/cmdb") 18 | client, err := mongo.Connect(ctx, opts) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | if err = client.Ping(ctx, nil); err != nil { 25 | log.Panicf("ping mongodb server error, %s", err) 26 | } 27 | 28 | return mongox.NewMongo(client, "cmdb-e2e") 29 | } 30 | -------------------------------------------------------------------------------- /internal/attribute/internal/integration/startup/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package startup 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/google/wire" 8 | ) 9 | 10 | func InitHandler() (*attribute.Handler, error) { 11 | wire.Build(InitMongoDB, 12 | attribute.InitModule, 13 | wire.FieldsOf(new(*attribute.Module), "Hdl"), 14 | ) 15 | return new(attribute.Handler), nil 16 | } 17 | -------------------------------------------------------------------------------- /internal/attribute/internal/integration/startup/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package startup 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/attribute" 11 | "github.com/Duke1616/ecmdb/internal/attribute/internal/web" 12 | ) 13 | 14 | // Injectors from wire.go: 15 | 16 | func InitHandler() (*web.Handler, error) { 17 | mongo := InitMongoDB() 18 | module, err := attribute.InitModule(mongo) 19 | if err != nil { 20 | return nil, err 21 | } 22 | handler := module.Hdl 23 | return handler, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/attribute/internal/repository/dao/init.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | func InitIndexes(db *mongox.Mongo) error { 12 | col := db.Collection(AttributeCollection) 13 | 14 | indexes := []mongo.IndexModel{ 15 | { 16 | Keys: bson.D{ 17 | {"field_uid", -1}, 18 | {"model_uid", -1}, 19 | }, 20 | Options: options.Index().SetUnique(true), 21 | }, 22 | { 23 | Keys: bson.M{"model_uid": -1}, 24 | }, 25 | } 26 | 27 | _, err := col.Indexes().CreateMany(context.Background(), indexes) 28 | 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /internal/attribute/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/attribute/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/attribute/module.go: -------------------------------------------------------------------------------- 1 | package attribute 2 | 3 | type Module struct { 4 | Svc Service 5 | Hdl *Handler 6 | } 7 | -------------------------------------------------------------------------------- /internal/attribute/types.go: -------------------------------------------------------------------------------- 1 | package attribute 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/attribute/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/attribute/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/attribute/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Attribute = domain.Attribute 14 | -------------------------------------------------------------------------------- /internal/attribute/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package attribute 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/attribute/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/attribute/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/attribute/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | "sync" 13 | ) 14 | 15 | var ProviderSet = wire.NewSet( 16 | web.NewHandler, 17 | repository.NewAttributeRepository, 18 | repository.NewAttributeGroupRepository, 19 | dao.NewAttributeGroupDAO) 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | NewService, 25 | InitAttributeDAO, 26 | wire.Struct(new(Module), "*"), 27 | ) 28 | return new(Module), nil 29 | } 30 | 31 | var daoOnce = sync.Once{} 32 | 33 | func InitCollectionOnce(db *mongox.Mongo) { 34 | daoOnce.Do(func() { 35 | err := dao.InitIndexes(db) 36 | if err != nil { 37 | panic(err) 38 | } 39 | }) 40 | } 41 | 42 | func InitAttributeDAO(db *mongox.Mongo) dao.AttributeDAO { 43 | InitCollectionOnce(db) 44 | return dao.NewAttributeDAO(db) 45 | } 46 | 47 | func NewService(repo repository.AttributeRepository, repoGroup repository.AttributeGroupRepository) Service { 48 | return service.NewService(repo, repoGroup) 49 | } 50 | -------------------------------------------------------------------------------- /internal/codebook/internal/domain/codebook.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Codebook struct { 4 | Id int64 5 | Name string 6 | Owner int64 7 | Code string 8 | Language string 9 | Secret string 10 | Identifier string 11 | } 12 | -------------------------------------------------------------------------------- /internal/codebook/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/codebook/internal/repository/error.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "github.com/Duke1616/ecmdb/internal/codebook/internal/repository/dao" 4 | 5 | var ErrUserNotFound = dao.ErrDataNotFound 6 | -------------------------------------------------------------------------------- /internal/codebook/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/codebook/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/codebook/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type CreateCodebookReq struct { 4 | Name string `json:"name"` 5 | Owner int64 `json:"owner"` 6 | Code string `json:"code"` 7 | Language string `json:"language"` 8 | Identifier string `json:"identifier"` 9 | } 10 | 11 | type DetailCodebookReq struct { 12 | Id int64 `json:"id"` 13 | } 14 | 15 | type UpdateCodebookReq struct { 16 | Id int64 `json:"id"` 17 | Name string `json:"name"` 18 | Owner int64 `json:"owner"` 19 | Code string `json:"code"` 20 | Language string `json:"language"` 21 | } 22 | 23 | type DeleteCodebookReq struct { 24 | Id int64 `json:"id"` 25 | } 26 | 27 | type Page struct { 28 | Offset int64 `json:"offset,omitempty"` 29 | Limit int64 `json:"limit,omitempty"` 30 | } 31 | 32 | type ListCodebookReq struct { 33 | Page 34 | } 35 | 36 | type Codebook struct { 37 | Id int64 `json:"id"` 38 | Name string `json:"name"` 39 | Owner int64 `json:"owner"` 40 | Identifier string `json:"identifier"` 41 | Code string `json:"code"` 42 | Language string `json:"language"` 43 | Secret string `json:"secret"` 44 | } 45 | 46 | type RetrieveCodebooks struct { 47 | Total int64 `json:"total"` 48 | Codebooks []Codebook `json:"codebooks"` 49 | } 50 | -------------------------------------------------------------------------------- /internal/codebook/module.go: -------------------------------------------------------------------------------- 1 | package codebook 2 | 3 | import "github.com/Duke1616/ecmdb/internal/codebook/internal/service" 4 | 5 | type Module struct { 6 | Hdl *Handler 7 | Svc service.Service 8 | } 9 | -------------------------------------------------------------------------------- /internal/codebook/types.go: -------------------------------------------------------------------------------- 1 | package codebook 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/codebook/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/codebook/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/codebook/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Codebook = domain.Codebook 14 | -------------------------------------------------------------------------------- /internal/codebook/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package codebook 4 | 5 | import ( 6 | repository "github.com/Duke1616/ecmdb/internal/codebook/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/codebook/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/codebook/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/codebook/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | ) 13 | 14 | var ProviderSet = wire.NewSet( 15 | web.NewHandler, 16 | service.NewService, 17 | repository.NewCodebookRepository, 18 | dao.NewCodebookDAO) 19 | 20 | func InitModule(db *mongox.Mongo) (*Module, error) { 21 | wire.Build( 22 | ProviderSet, 23 | wire.Struct(new(Module), "*"), 24 | ) 25 | return new(Module), nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/codebook/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package codebook 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/codebook/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/codebook/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/codebook/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/codebook/internal/web" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/google/wire" 16 | ) 17 | 18 | // Injectors from wire.go: 19 | 20 | func InitModule(db *mongox.Mongo) (*Module, error) { 21 | codebookDAO := dao.NewCodebookDAO(db) 22 | codebookRepository := repository.NewCodebookRepository(codebookDAO) 23 | serviceService := service.NewService(codebookRepository) 24 | handler := web.NewHandler(serviceService) 25 | module := &Module{ 26 | Hdl: handler, 27 | Svc: serviceService, 28 | } 29 | return module, nil 30 | } 31 | 32 | // wire.go: 33 | 34 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewCodebookRepository, dao.NewCodebookDAO) 35 | -------------------------------------------------------------------------------- /internal/department/internal/domain/department.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Department struct { 4 | Id int64 5 | Pid int64 6 | Name string 7 | Sort int64 8 | Enabled bool 9 | // 部分负责人,可能有多个人 10 | Leaders []int64 11 | // 分管领导,部门最大的领导 12 | MainLeader int64 13 | } 14 | -------------------------------------------------------------------------------- /internal/department/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/department/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/department/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/department/internal/web/tree.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/department/internal/domain" 5 | "sort" 6 | ) 7 | 8 | func GetDepartmentsTree(ms []domain.Department) []*Department { 9 | // 将菜单转换为 *Menu 类型并存入 map 10 | allMap := make(map[int64]*Department, len(ms)) 11 | var list []*Department 12 | 13 | for _, m := range ms { 14 | voMenu := toVoMenu(m) 15 | voMenu.Children = []*Department{} 16 | allMap[m.Id] = voMenu 17 | if m.Pid == 0 { 18 | list = append(list, voMenu) 19 | } 20 | } 21 | 22 | // 构建菜单树 23 | for _, m := range ms { 24 | if parent, exists := allMap[m.Pid]; exists { 25 | parent.Children = append(parent.Children, allMap[m.Id]) 26 | } 27 | } 28 | 29 | // 对菜单树进行排序 30 | sortDepartment(list) 31 | return list 32 | } 33 | 34 | func sortDepartment(menus []*Department) { 35 | sort.Slice(menus, func(i, j int) bool { 36 | return menus[i].Sort < menus[j].Sort 37 | }) 38 | 39 | for _, m := range menus { 40 | if len(m.Children) > 0 { 41 | sort.Slice(m.Children, func(i, j int) bool { 42 | return m.Children[i].Sort < m.Children[j].Sort 43 | }) 44 | sortDepartment(m.Children) 45 | } 46 | } 47 | } 48 | 49 | func toVoMenu(req domain.Department) *Department { 50 | return &Department{ 51 | Id: req.Id, 52 | Pid: req.Pid, 53 | Sort: req.Sort, 54 | Name: req.Name, 55 | Enabled: req.Enabled, 56 | Leaders: req.Leaders, 57 | MainLeader: req.MainLeader, 58 | Children: []*Department{}, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/department/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type CreateDepartmentReq struct { 4 | Pid int64 `json:"pid"` 5 | Name string `json:"name"` 6 | Sort int64 `json:"sort"` 7 | Enabled bool `json:"enabled"` 8 | Leaders []int64 `json:"leaders"` 9 | MainLeader int64 `json:"main_leader"` 10 | } 11 | 12 | type DeleteDepartmentReq struct { 13 | Id int64 `json:"id"` 14 | } 15 | 16 | type UpdateDepartmentReq struct { 17 | Id int64 `json:"id"` 18 | Pid int64 `json:"pid"` 19 | Name string `json:"name"` 20 | Sort int64 `json:"sort"` 21 | Enabled bool `json:"enabled"` 22 | Leaders []int64 `json:"leaders"` 23 | MainLeader int64 `json:"main_leader"` 24 | } 25 | 26 | type Department struct { 27 | Id int64 `json:"id"` 28 | Pid int64 `json:"pid"` 29 | Name string `json:"name"` 30 | Sort int64 `json:"sort"` 31 | Enabled bool `json:"enabled"` 32 | Leaders []int64 `json:"leaders"` 33 | MainLeader int64 `json:"main_leader"` 34 | Children []*Department `json:"children"` 35 | } 36 | -------------------------------------------------------------------------------- /internal/department/module.go: -------------------------------------------------------------------------------- 1 | package department 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/department/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/department/internal/web" 6 | ) 7 | 8 | type Module struct { 9 | Hdl *web.Handler 10 | Svc service.Service 11 | } 12 | -------------------------------------------------------------------------------- /internal/department/types.go: -------------------------------------------------------------------------------- 1 | package department 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/department/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/department/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/department/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Department = domain.Department 12 | 13 | type Service = service.Service 14 | -------------------------------------------------------------------------------- /internal/department/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package department 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/department/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/department/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/department/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/department/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | ) 13 | 14 | var ProviderSet = wire.NewSet( 15 | web.NewHandler, 16 | service.NewService, 17 | repository.NewDepartmentRepository, 18 | dao.NewDepartmentDAO, 19 | ) 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | wire.Struct(new(Module), "*"), 25 | ) 26 | return new(Module), nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/department/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package department 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/department/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/department/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/department/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/department/internal/web" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/google/wire" 16 | ) 17 | 18 | // Injectors from wire.go: 19 | 20 | func InitModule(db *mongox.Mongo) (*Module, error) { 21 | departmentDAO := dao.NewDepartmentDAO(db) 22 | departmentRepository := repository.NewDepartmentRepository(departmentDAO) 23 | serviceService := service.NewService(departmentRepository) 24 | handler := web.NewHandler(serviceService) 25 | module := &Module{ 26 | Hdl: handler, 27 | Svc: serviceService, 28 | } 29 | return module, nil 30 | } 31 | 32 | // wire.go: 33 | 34 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewDepartmentRepository, dao.NewDepartmentDAO) 35 | -------------------------------------------------------------------------------- /internal/discovery/internal/domain/discovery.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | // Discovery 模版自动化节点、自动发现调度节点 4 | type Discovery struct { 5 | Id int64 6 | TemplateId int64 7 | RunnerId int64 8 | Field string 9 | Value string 10 | } 11 | -------------------------------------------------------------------------------- /internal/discovery/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/discovery/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/discovery/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/discovery/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type CreateDiscoveryReq struct { 4 | TemplateId int64 `json:"template_id"` 5 | RunnerId int64 `json:"runner_id"` 6 | Field string `json:"field"` 7 | Value string `json:"value"` 8 | } 9 | 10 | type UpdateDiscoveryReq struct { 11 | Id int64 `json:"id"` 12 | RunnerId int64 `json:"runner_id"` 13 | Field string `json:"field"` 14 | Value string `json:"value"` 15 | } 16 | 17 | type Page struct { 18 | Offset int64 `json:"offset,omitempty"` 19 | Limit int64 `json:"limit,omitempty"` 20 | } 21 | 22 | type ListByTemplateId struct { 23 | Page 24 | TemplateId int64 `json:"template_id"` 25 | } 26 | 27 | type Discovery struct { 28 | Id int64 `json:"id"` 29 | TemplateId int64 `json:"template_id"` 30 | RunnerId int64 `json:"runner_id"` 31 | Field string `json:"field"` 32 | Value string `json:"value"` 33 | } 34 | 35 | type RetrieveDiscoveries struct { 36 | Total int64 `json:"total"` 37 | Discoveries []Discovery `json:"discoveries"` 38 | } 39 | 40 | type SyncDiscoveryReq struct { 41 | TemplateId int64 `json:"template_id"` 42 | TemplateGroupId int64 `json:"template_group_id"` 43 | SyncTemplateId int64 `json:"sync_template_id"` 44 | } 45 | 46 | type DeleteDiscoveryReq struct { 47 | Id int64 `json:"id"` 48 | } 49 | -------------------------------------------------------------------------------- /internal/discovery/module.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | type Module struct { 4 | Svc Service 5 | Hdl *Handler 6 | } 7 | -------------------------------------------------------------------------------- /internal/discovery/types.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/discovery/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/discovery/internal/web" 6 | ) 7 | 8 | type Handler = web.Handler 9 | 10 | type Service = service.Service 11 | -------------------------------------------------------------------------------- /internal/discovery/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package discovery 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/discovery/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/discovery/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/discovery/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/discovery/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | ) 13 | 14 | var ProviderSet = wire.NewSet( 15 | web.NewHandler, 16 | service.NewService, 17 | repository.NewDiscoveryRepository, 18 | dao.NewDiscoveryDAO, 19 | ) 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | wire.Struct(new(Module), "*"), 25 | ) 26 | return new(Module), nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/discovery/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package discovery 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/discovery/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/discovery/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/discovery/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/discovery/internal/web" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/google/wire" 16 | ) 17 | 18 | // Injectors from wire.go: 19 | 20 | func InitModule(db *mongox.Mongo) (*Module, error) { 21 | discoveryDAO := dao.NewDiscoveryDAO(db) 22 | discoveryRepository := repository.NewDiscoveryRepository(discoveryDAO) 23 | serviceService := service.NewService(discoveryRepository) 24 | handler := web.NewHandler(serviceService) 25 | module := &Module{ 26 | Svc: serviceService, 27 | Hdl: handler, 28 | } 29 | return module, nil 30 | } 31 | 32 | // wire.go: 33 | 34 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewDiscoveryRepository, dao.NewDiscoveryDAO) 35 | -------------------------------------------------------------------------------- /internal/endpoint/internal/domain/endpoint.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Endpoint struct { 4 | Id int64 5 | Path string 6 | Method string 7 | Resource string 8 | Desc string 9 | IsAuth bool 10 | IsAudit bool 11 | IsPermission bool 12 | } 13 | -------------------------------------------------------------------------------- /internal/endpoint/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/endpoint/internal/repository/dao/init.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | func InitIndexes(db *mongox.Mongo) error { 12 | col := db.Collection(EndpointCollection) 13 | 14 | indexes := []mongo.IndexModel{ 15 | { 16 | Keys: bson.D{{Key: "path", Value: "text"}}, 17 | Options: options.Index().SetDefaultLanguage("english"), 18 | }, 19 | } 20 | 21 | _, err := col.Indexes().CreateMany(context.Background(), indexes) 22 | 23 | return err 24 | } 25 | -------------------------------------------------------------------------------- /internal/endpoint/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/endpoint/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type RegisterEndpointReq struct { 4 | Path string `bson:"path"` 5 | Method string `bson:"method"` 6 | Resource string `bson:"resource"` 7 | Desc string `bson:"desc"` 8 | IsAuth bool `bson:"is_auth"` 9 | IsAudit bool `bson:"is_audit"` 10 | IsPermission bool `bson:"is_permission"` 11 | } 12 | 13 | type RegisterEndpointsReq struct { 14 | RegisterEndpoint []RegisterEndpointReq `json:"register_endpoint"` 15 | } 16 | 17 | type Endpoint struct { 18 | Id int64 `json:"id"` 19 | Path string `json:"path"` 20 | Method string `json:"method"` 21 | Resource string `json:"resource"` 22 | Desc string `json:"desc"` 23 | IsAuth bool `json:"is_auth"` 24 | IsAudit bool `json:"is_audit"` 25 | IsPermission bool `json:"is_permission"` 26 | } 27 | 28 | type Page struct { 29 | Offset int64 `json:"offset,omitempty"` 30 | Limit int64 `json:"limit,omitempty"` 31 | } 32 | 33 | type FilterPathReq struct { 34 | Page 35 | Path string `json:"path"` 36 | } 37 | 38 | type RetrieveEndpoints struct { 39 | Endpoints []Endpoint `json:"endpoints"` 40 | Total int64 `json:"total"` 41 | } 42 | -------------------------------------------------------------------------------- /internal/endpoint/module.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | Svc Service 6 | } 7 | -------------------------------------------------------------------------------- /internal/endpoint/types.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Endpoint = domain.Endpoint 14 | -------------------------------------------------------------------------------- /internal/endpoint/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package endpoint 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | "sync" 13 | ) 14 | 15 | var ProviderSet = wire.NewSet( 16 | web.NewHandler, 17 | service.NewService, 18 | repository.NewEndpointRepository, 19 | ) 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | InitEndpointDAO, 25 | wire.Struct(new(Module), "*"), 26 | ) 27 | return new(Module), nil 28 | } 29 | 30 | var daoOnce = sync.Once{} 31 | 32 | func InitCollectionOnce(db *mongox.Mongo) { 33 | daoOnce.Do(func() { 34 | err := dao.InitIndexes(db) 35 | if err != nil { 36 | panic(err) 37 | } 38 | }) 39 | } 40 | 41 | func InitEndpointDAO(db *mongox.Mongo) dao.EndpointDAO { 42 | InitCollectionOnce(db) 43 | return dao.NewEndpointDAO(db) 44 | } 45 | -------------------------------------------------------------------------------- /internal/endpoint/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package endpoint 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/endpoint/internal/web" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/google/wire" 16 | "sync" 17 | ) 18 | 19 | // Injectors from wire.go: 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | endpointDAO := InitEndpointDAO(db) 23 | endpointRepository := repository.NewEndpointRepository(endpointDAO) 24 | serviceService := service.NewService(endpointRepository) 25 | handler := web.NewHandler(serviceService) 26 | module := &Module{ 27 | Hdl: handler, 28 | Svc: serviceService, 29 | } 30 | return module, nil 31 | } 32 | 33 | // wire.go: 34 | 35 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewEndpointRepository) 36 | 37 | var daoOnce = sync.Once{} 38 | 39 | func InitCollectionOnce(db *mongox.Mongo) { 40 | daoOnce.Do(func() { 41 | err := dao.InitIndexes(db) 42 | if err != nil { 43 | panic(err) 44 | } 45 | }) 46 | } 47 | 48 | func InitEndpointDAO(db *mongox.Mongo) dao.EndpointDAO { 49 | InitCollectionOnce(db) 50 | return dao.NewEndpointDAO(db) 51 | } 52 | -------------------------------------------------------------------------------- /internal/engine/internal/domain/instance.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/Bunny3th/easy-workflow/workflow/database" 5 | ) 6 | 7 | type Instance struct { 8 | TaskID int //任务ID 9 | ProcInstID int //流程实例ID 10 | ProcID int //流程ID 11 | ProcName string //流程名称 12 | ProcVersion int //流程版本号 13 | BusinessID string //业务ID 14 | Starter string //流程发起人用户名称 15 | CurrentNodeID string //当前进行节点ID 16 | CurrentNodeName string //当前进行节点名称 17 | CreateTime *database.LocalTime //创建时间 18 | ApprovedBy string //当前处理人 19 | Status int //0:未完成(审批中) 1:已完成(通过) 2:撤销 20 | } 21 | -------------------------------------------------------------------------------- /internal/engine/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ValidationError = ErrorCode{Code: 503002, Msg: "验证错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/engine/internal/web/handler.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/Bunny3th/easy-workflow/workflow/engine" 6 | "github.com/Duke1616/ecmdb/internal/engine/internal/service" 7 | "github.com/Duke1616/ecmdb/pkg/ginx" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type Handler struct { 12 | svc service.Service 13 | } 14 | 15 | func NewHandler(svc service.Service) *Handler { 16 | return &Handler{ 17 | svc: svc, 18 | } 19 | } 20 | 21 | func (h *Handler) PrivateRoutes(server *gin.Engine) { 22 | g := server.Group("/api/engine") 23 | g.POST("/task/pass", ginx.WrapBody[Pass](h.Pass)) 24 | } 25 | 26 | func (h *Handler) Pass(ctx *gin.Context, req Pass) (ginx.Result, error) { 27 | variables, err := json.Marshal(req.Variables) 28 | if err != nil { 29 | return systemErrorResult, err 30 | } 31 | 32 | err = engine.TaskPass(req.TaskId, req.Comment, string(variables), false) 33 | if err != nil { 34 | return systemErrorResult, err 35 | } 36 | 37 | return ginx.Result{}, nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/engine/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/engine/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | validateErrorResult = ginx.Result{ 15 | Code: errs.ValidationError.Code, 16 | Msg: errs.ValidationError.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/engine/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type Pass struct { 4 | TaskId int `json:"task_id"` 5 | Comment string `json:"comment"` 6 | Variables []Variable `json:"variables"` 7 | } 8 | 9 | type Variable struct { 10 | Key string `json:"key"` 11 | Value string `json:"value"` 12 | } 13 | -------------------------------------------------------------------------------- /internal/engine/module.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/engine/internal/web" 5 | ) 6 | 7 | type Module struct { 8 | Svc Service 9 | Hdl *web.Handler 10 | } 11 | -------------------------------------------------------------------------------- /internal/engine/types.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/engine/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/engine/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/engine/internal/web" 7 | ) 8 | 9 | type Service = service.Service 10 | 11 | type Handler = web.Handler 12 | 13 | type Instance = domain.Instance 14 | -------------------------------------------------------------------------------- /internal/engine/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package engine 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/engine/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/engine/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/engine/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/engine/internal/web" 10 | "github.com/google/wire" 11 | "gorm.io/gorm" 12 | ) 13 | 14 | var ProviderSet = wire.NewSet( 15 | web.NewHandler, 16 | service.NewService, 17 | repository.NewProcessEngineRepository, 18 | dao.NewProcessEngineDAO, 19 | ) 20 | 21 | func InitModule(db *gorm.DB) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | wire.Struct(new(Module), "*"), 25 | ) 26 | return new(Module), nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/engine/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package engine 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/engine/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/engine/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/engine/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/engine/internal/web" 14 | "github.com/google/wire" 15 | "gorm.io/gorm" 16 | ) 17 | 18 | // Injectors from wire.go: 19 | 20 | func InitModule(db *gorm.DB) (*Module, error) { 21 | processEngineDAO := dao.NewProcessEngineDAO(db) 22 | processEngineRepository := repository.NewProcessEngineRepository(processEngineDAO) 23 | serviceService := service.NewService(processEngineRepository) 24 | handler := web.NewHandler(serviceService) 25 | module := &Module{ 26 | Svc: serviceService, 27 | Hdl: handler, 28 | } 29 | return module, nil 30 | } 31 | 32 | // wire.go: 33 | 34 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewProcessEngineRepository, dao.NewProcessEngineDAO) 35 | -------------------------------------------------------------------------------- /internal/event/domain/notification.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/Bunny3th/easy-workflow/workflow/model" 5 | "github.com/Duke1616/ecmdb/internal/order" 6 | "github.com/Duke1616/ecmdb/internal/workflow" 7 | "github.com/Duke1616/enotify/notify/feishu/card" 8 | ) 9 | 10 | type NodeName string 11 | 12 | const ( 13 | Start NodeName = "START" // 开始节点 14 | Automation NodeName = "AUTOMATION" // 自动化节点 15 | User NodeName = "USER" // 用户审批节点 16 | ) 17 | 18 | type StrategyInfo struct { 19 | NodeName NodeName `json:"node_name"` // 节点名称 20 | OrderInfo order.Order `json:"order_info"` // 工单提交信息 21 | WfInfo workflow.Workflow `json:"wf_info"` // 流程信息 22 | InstanceId int `json:"instance_id"` // 实例 id 23 | CurrentNode *model.Node `json:"current_node"` // 流程当前节点 24 | } 25 | 26 | type Notification struct { 27 | Receiver string `json:"receiver"` // 接收者(手机/邮箱/用户ID) 28 | Template Template `json:"template"` // 发送模版 29 | Channel Channel `json:"channel"` // 发送渠道 30 | } 31 | 32 | type Template struct { 33 | Name string `json:"name"` // 模版名称 34 | Title string `json:"title"` // 模版标题 35 | Fields []card.Field `json:"fields"` // 模版字段信息 36 | Values []card.Value `json:"values"` // 模版传递变量 37 | HideForm bool `json:"hide_form"` // 隐藏 38 | } 39 | -------------------------------------------------------------------------------- /internal/event/domain/provider.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Channel string 4 | 5 | const ( 6 | ChannelFeishuCard Channel = "FEISHU_CARD" // 飞书卡片 7 | ) 8 | -------------------------------------------------------------------------------- /internal/event/module.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/event/service/easyflow" 5 | ) 6 | 7 | type Module struct { 8 | Event *easyflow.ProcessEvent 9 | } 10 | -------------------------------------------------------------------------------- /internal/event/producer/producer.go: -------------------------------------------------------------------------------- 1 | package producer 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mqx" 6 | "github.com/ecodeclub/mq-api" 7 | ) 8 | 9 | type OrderStatusModifyEventProducer interface { 10 | Produce(ctx context.Context, evt OrderStatusModifyEvent) error 11 | } 12 | 13 | func NewOrderStatusModifyEventProducer(q mq.MQ) (OrderStatusModifyEventProducer, error) { 14 | return mqx.NewGeneralProducer[OrderStatusModifyEvent](q, OrderStatusModifyEventName) 15 | } 16 | -------------------------------------------------------------------------------- /internal/event/producer/types.go: -------------------------------------------------------------------------------- 1 | package producer 2 | 3 | const ( 4 | OrderStatusModifyEventName = "order_status_modify_events" 5 | ) 6 | 7 | type Status uint8 8 | 9 | func (s Status) ToUint8() uint8 { 10 | return uint8(s) 11 | } 12 | 13 | const ( 14 | // START 等待开始 15 | START Status = 1 16 | // PROCESS 流程运行中 17 | PROCESS Status = 2 18 | // END 完成 19 | END Status = 3 20 | // RETRY 重试 21 | RETRY Status = 4 22 | ) 23 | 24 | type OrderStatusModifyEvent struct { 25 | ProcessInstanceId int `json:"process_instance_id"` 26 | Status Status `json:"status"` 27 | } 28 | -------------------------------------------------------------------------------- /internal/event/service/channel/base.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/internal/event/domain" 7 | "github.com/Duke1616/ecmdb/internal/event/service/provider" 8 | ) 9 | 10 | type baseChannel struct { 11 | builder provider.SelectorBuilder 12 | } 13 | 14 | func (s *baseChannel) Send(ctx context.Context, notification domain.Notification) (bool, error) { 15 | selector, err := s.builder.Build() 16 | if err != nil { 17 | return false, fmt.Errorf("%s: %w", "发送通知失败", err) 18 | } 19 | 20 | for { 21 | p, err1 := selector.Next(ctx, notification) 22 | if err1 != nil { 23 | return false, fmt.Errorf("%s: %w", "发送通知失败", err1) 24 | } 25 | 26 | return p.Send(ctx, notification) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/event/service/channel/channel.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/internal/event/domain" 7 | ) 8 | 9 | type Channel interface { 10 | // Send 发送通知 11 | Send(ctx context.Context, notification domain.Notification) (bool, error) 12 | } 13 | 14 | // Dispatcher 渠道分发器,对外伪装成Channel,作为统一入口 15 | type Dispatcher struct { 16 | channels map[domain.Channel]Channel 17 | } 18 | 19 | // NewDispatcher 创建渠道分发器 20 | func NewDispatcher(channels map[domain.Channel]Channel) *Dispatcher { 21 | return &Dispatcher{ 22 | channels: channels, 23 | } 24 | } 25 | 26 | func (d *Dispatcher) Send(ctx context.Context, notification domain.Notification) (bool, error) { 27 | channel, ok := d.channels[notification.Channel] 28 | if !ok { 29 | return false, fmt.Errorf("%s: %s", "无可用通知渠道", notification.Channel) 30 | } 31 | return channel.Send(ctx, notification) 32 | } 33 | -------------------------------------------------------------------------------- /internal/event/service/channel/feishu_card.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/event/service/provider" 5 | ) 6 | 7 | type feishuCardChannel struct { 8 | baseChannel 9 | } 10 | 11 | func NewFeishuCardChannel(builder provider.SelectorBuilder) Channel { 12 | return &feishuCardChannel{ 13 | baseChannel{ 14 | builder: builder, 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/event/service/provider/sequential/sequential.go: -------------------------------------------------------------------------------- 1 | package sequential 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/internal/event/domain" 7 | "github.com/Duke1616/ecmdb/internal/event/service/provider" 8 | ) 9 | 10 | var ( 11 | _ provider.Selector = (*selector)(nil) 12 | _ provider.SelectorBuilder = (*SelectorBuilder)(nil) 13 | ) 14 | 15 | // selector 供应商顺序选择器 16 | type selector struct { 17 | idx int 18 | providers []provider.Provider 19 | } 20 | 21 | func (r *selector) Next(_ context.Context, _ domain.Notification) (provider.Provider, error) { 22 | if len(r.providers) == r.idx { 23 | return nil, fmt.Errorf("%s", "无可用渠道") 24 | } 25 | 26 | p := r.providers[r.idx] 27 | r.idx++ 28 | return p, nil 29 | } 30 | 31 | type SelectorBuilder struct { 32 | providers []provider.Provider 33 | } 34 | 35 | func NewSelectorBuilder(providers []provider.Provider) *SelectorBuilder { 36 | return &SelectorBuilder{providers: providers} 37 | } 38 | 39 | func (s *SelectorBuilder) Build() (provider.Selector, error) { 40 | return &selector{ 41 | providers: s.providers, 42 | }, nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/event/service/provider/types.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/event/domain" 6 | ) 7 | 8 | type Provider interface { 9 | // Send 发送消息 10 | Send(ctx context.Context, notification domain.Notification) (bool, error) 11 | } 12 | 13 | // Selector 供应商选择器接口 14 | type Selector interface { 15 | // Next 获取下一个供应商,无可用供应商时返回错误 16 | Next(ctx context.Context, notification domain.Notification) (Provider, error) 17 | } 18 | 19 | // SelectorBuilder 供应商选择器的构造器 20 | type SelectorBuilder interface { 21 | // Build 构造选择器,可以在Build方法上添加参数来构建更复杂的选择器 22 | Build() (Selector, error) 23 | } 24 | -------------------------------------------------------------------------------- /internal/event/service/strategy/result.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/Duke1616/ecmdb/internal/task" 8 | ) 9 | 10 | type FetcherResult interface { 11 | FetchResult(ctx context.Context, instanceID int, nodeID string) (map[string]interface{}, error) 12 | } 13 | 14 | type TaskResult struct { 15 | taskSvc task.Service 16 | } 17 | 18 | func NewResult(taskSvc task.Service) FetcherResult { 19 | return &TaskResult{ 20 | taskSvc: taskSvc, 21 | } 22 | } 23 | 24 | func (r *TaskResult) FetchResult(ctx context.Context, instanceID int, nodeID string) (map[string]interface{}, error) { 25 | result, err := r.taskSvc.FindTaskResult(ctx, instanceID, nodeID) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | if result.WantResult == "" { 31 | return nil, fmt.Errorf("返回值为空, 不做任何数据处理") 32 | } 33 | 34 | var wantResult map[string]interface{} 35 | err = json.Unmarshal([]byte(result.WantResult), &wantResult) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | return wantResult, nil 41 | } 42 | -------------------------------------------------------------------------------- /internal/event/service/strategy/wrap_send.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/enotify/notify" 7 | "sync" 8 | ) 9 | 10 | const ( 11 | // ProcessEndSend 流程结束后发送 12 | ProcessEndSend = 1 13 | // ProcessNowSend 当前节点通过直接发送 14 | ProcessNowSend = 2 15 | ) 16 | 17 | func send(ctx context.Context, notifyWrap []notify.NotifierWrap) (bool, error) { 18 | var wg sync.WaitGroup 19 | var mu sync.Mutex 20 | var firstError error 21 | success := true 22 | 23 | // 使用 goroutines 发送消息 24 | for _, msg := range notifyWrap { 25 | wg.Add(1) 26 | nw := msg 27 | go func(m notify.NotifierWrap) { 28 | defer wg.Done() 29 | 30 | ok, err := nw.Send(ctx) 31 | if err != nil { 32 | mu.Lock() // 锁定访问共享资源 33 | if firstError == nil { 34 | firstError = err // 记录第一个出现的错误 35 | } 36 | success = false 37 | mu.Unlock() 38 | } 39 | 40 | if !ok { 41 | mu.Lock() 42 | if firstError == nil { 43 | firstError = fmt.Errorf("消息发送失败") 44 | } 45 | success = false 46 | mu.Unlock() 47 | } 48 | }(msg) 49 | } 50 | 51 | // 等待所有 goroutine 完成 52 | wg.Wait() 53 | 54 | if firstError != nil { 55 | return false, firstError 56 | } 57 | return success, nil 58 | } 59 | -------------------------------------------------------------------------------- /internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | -------------------------------------------------------------------------------- /internal/menu/internal/domain/menu.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Type uint8 4 | 5 | func (s Type) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // DIR 目录 11 | DIR Type = 1 12 | // MENU 菜单 13 | MENU Type = 2 14 | // BUTTON 按钮 15 | BUTTON Type = 3 16 | ) 17 | 18 | type Status uint8 19 | 20 | func (s Status) ToUint8() uint8 { 21 | return uint8(s) 22 | } 23 | 24 | const ( 25 | // ENABLED 启用 26 | ENABLED Status = 1 27 | // DISABLED 禁用 28 | DISABLED Status = 2 29 | ) 30 | 31 | type Menu struct { 32 | Id int64 33 | Pid int64 34 | Path string 35 | Name string 36 | Sort int64 37 | Component string 38 | Redirect string 39 | Status Status 40 | Type Type 41 | Meta Meta 42 | Endpoints []Endpoint 43 | } 44 | 45 | type Endpoint struct { 46 | Path string 47 | Method string 48 | Desc string 49 | } 50 | 51 | type Meta struct { 52 | Title string // 展示名称 53 | IsHidden bool // 是否展示 54 | IsAffix bool // 是否固定 55 | IsKeepAlive bool // 是否缓存 56 | Icon string // Icon图标 57 | } 58 | -------------------------------------------------------------------------------- /internal/menu/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/menu/internal/event/producer.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mqx" 6 | "github.com/ecodeclub/mq-api" 7 | ) 8 | 9 | type MenuChangeEventProducer interface { 10 | Produce(ctx context.Context, evt MenuEvent) error 11 | } 12 | 13 | func NewMenuChangeEventProducer(q mq.MQ) (MenuChangeEventProducer, error) { 14 | return mqx.NewGeneralProducer[MenuEvent](q, MenuChangeEventName) 15 | } 16 | -------------------------------------------------------------------------------- /internal/menu/internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | const ( 4 | // MenuChangeEventName 菜单变更事件 5 | MenuChangeEventName = "menu_change_events" 6 | ) 7 | 8 | type Action uint8 9 | 10 | func (s Action) ToUint8() uint8 { 11 | return uint8(s) 12 | } 13 | 14 | const ( 15 | // CREATE 创建动作,比如 ADMIN 超级管理员自动权限录入 16 | // TODO 拥有根菜单权限录入 17 | CREATE Action = 1 18 | // WRITE 写入动作 19 | WRITE Action = 2 20 | // REWRITE 全部删除、重新录入数据 21 | REWRITE Action = 3 22 | // DELETE 全部删除、重新录入数据 23 | DELETE Action = 4 24 | ) 25 | 26 | type MenuEvent struct { 27 | Menu Menu `json:"menu"` 28 | Action Action `json:"action"` 29 | } 30 | 31 | type Menu struct { 32 | Id int64 `json:"id"` 33 | Endpoints []Endpoint `json:"endpoints"` 34 | } 35 | 36 | type Endpoint struct { 37 | Path string `json:"path"` 38 | Method string `json:"method"` 39 | } 40 | -------------------------------------------------------------------------------- /internal/menu/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/menu/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/menu/module.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | Svc Service 6 | } 7 | -------------------------------------------------------------------------------- /internal/menu/types.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/menu/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/menu/internal/event" 6 | "github.com/Duke1616/ecmdb/internal/menu/internal/service" 7 | "github.com/Duke1616/ecmdb/internal/menu/internal/web" 8 | ) 9 | 10 | type Handler = web.Handler 11 | 12 | type Service = service.Service 13 | 14 | type Menu = domain.Menu 15 | 16 | type Endpoint = domain.Endpoint 17 | 18 | type EventMenuQueue = event.MenuEvent 19 | 20 | type EventMenu = event.Menu 21 | 22 | type EventEndpoint = event.Endpoint 23 | -------------------------------------------------------------------------------- /internal/menu/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package menu 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/menu/internal/event" 7 | "github.com/Duke1616/ecmdb/internal/menu/internal/repository" 8 | "github.com/Duke1616/ecmdb/internal/menu/internal/repository/dao" 9 | "github.com/Duke1616/ecmdb/internal/menu/internal/service" 10 | "github.com/Duke1616/ecmdb/internal/menu/internal/web" 11 | "github.com/Duke1616/ecmdb/pkg/mongox" 12 | "github.com/ecodeclub/mq-api" 13 | "github.com/google/wire" 14 | ) 15 | 16 | var ProviderSet = wire.NewSet( 17 | web.NewHandler, 18 | service.NewService, 19 | repository.NewMenuRepository, 20 | dao.NewMenuDAO, 21 | ) 22 | 23 | func InitModule(q mq.MQ, db *mongox.Mongo) (*Module, error) { 24 | wire.Build( 25 | event.NewMenuChangeEventProducer, 26 | ProviderSet, 27 | wire.Struct(new(Module), "*"), 28 | ) 29 | return new(Module), nil 30 | } 31 | -------------------------------------------------------------------------------- /internal/menu/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package menu 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/menu/internal/event" 11 | "github.com/Duke1616/ecmdb/internal/menu/internal/repository" 12 | "github.com/Duke1616/ecmdb/internal/menu/internal/repository/dao" 13 | "github.com/Duke1616/ecmdb/internal/menu/internal/service" 14 | "github.com/Duke1616/ecmdb/internal/menu/internal/web" 15 | "github.com/Duke1616/ecmdb/pkg/mongox" 16 | "github.com/ecodeclub/mq-api" 17 | "github.com/google/wire" 18 | ) 19 | 20 | // Injectors from wire.go: 21 | 22 | func InitModule(q mq.MQ, db *mongox.Mongo) (*Module, error) { 23 | menuDAO := dao.NewMenuDAO(db) 24 | menuRepository := repository.NewMenuRepository(menuDAO) 25 | menuChangeEventProducer, err := event.NewMenuChangeEventProducer(q) 26 | if err != nil { 27 | return nil, err 28 | } 29 | serviceService := service.NewService(menuRepository, menuChangeEventProducer) 30 | handler := web.NewHandler(serviceService) 31 | module := &Module{ 32 | Hdl: handler, 33 | Svc: serviceService, 34 | } 35 | return module, nil 36 | } 37 | 38 | // wire.go: 39 | 40 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewMenuRepository, dao.NewMenuDAO) 41 | -------------------------------------------------------------------------------- /internal/model/internal/domain/model.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "time" 4 | 5 | type Model struct { 6 | ID int64 7 | GroupId int64 8 | Name string 9 | UID string 10 | Icon string 11 | Ctime time.Time 12 | Utime time.Time 13 | } 14 | 15 | type ModelGroup struct { 16 | ID int64 17 | Name string 18 | } 19 | -------------------------------------------------------------------------------- /internal/model/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 501001, Msg: "系统错误"} 5 | RelationIsNotFountResult = ErrorCode{Code: 501001, Msg: "模型关联关系不为空"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | 13 | func (e ErrorCode) Error() string { 14 | //TODO implement me 15 | panic("implement me") 16 | } 17 | -------------------------------------------------------------------------------- /internal/model/internal/integration/startup/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package startup 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/Duke1616/ecmdb/internal/model" 8 | "github.com/Duke1616/ecmdb/internal/relation" 9 | "github.com/Duke1616/ecmdb/internal/resource" 10 | "github.com/Duke1616/ecmdb/internal/test/ioc" 11 | "github.com/google/wire" 12 | ) 13 | 14 | func InitHandler(rmModule *relation.Module, attrModule *attribute.Module, resourceModule *resource.Module) (*model.Handler, error) { 15 | wire.Build(ioc.InitMongoDB, 16 | model.InitModule, 17 | wire.FieldsOf(new(*model.Module), "Hdl"), 18 | ) 19 | return new(model.Handler), nil 20 | } 21 | -------------------------------------------------------------------------------- /internal/model/internal/integration/startup/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package startup 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/attribute" 11 | "github.com/Duke1616/ecmdb/internal/model" 12 | "github.com/Duke1616/ecmdb/internal/model/internal/web" 13 | "github.com/Duke1616/ecmdb/internal/relation" 14 | "github.com/Duke1616/ecmdb/internal/resource" 15 | "github.com/Duke1616/ecmdb/internal/test/ioc" 16 | ) 17 | 18 | // Injectors from wire.go: 19 | 20 | func InitHandler(rmModule *relation.Module, attrModule *attribute.Module, resourceModule *resource.Module) (*web.Handler, error) { 21 | mongo := ioc.InitMongoDB() 22 | module, err := model.InitModule(mongo, rmModule, attrModule, resourceModule) 23 | if err != nil { 24 | return nil, err 25 | } 26 | handler := module.Hdl 27 | return handler, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/model/internal/repository/dao/type.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | const ( 4 | ModelCollection = "c_model" 5 | ModelGroupCollection = "c_model_group" 6 | ) 7 | 8 | type Model struct { 9 | Id int64 `bson:"id"` 10 | ModelGroupId int64 `bson:"model_group_id"` 11 | Name string `bson:"name"` 12 | UID string `bson:"uid"` 13 | Icon string `bson:"icon"` 14 | Ctime int64 `bson:"ctime"` 15 | Utime int64 `bson:"utime"` 16 | } 17 | 18 | type ModelGroup struct { 19 | Id int64 `bson:"id"` 20 | Name string `bson:"name"` 21 | Ctime int64 `bson:"ctime"` 22 | Utime int64 `bson:"utime"` 23 | } 24 | -------------------------------------------------------------------------------- /internal/model/internal/service/group.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/model/internal/domain" 6 | "github.com/Duke1616/ecmdb/internal/model/internal/repository" 7 | "golang.org/x/sync/errgroup" 8 | ) 9 | 10 | type MGService interface { 11 | Create(ctx context.Context, req domain.ModelGroup) (int64, error) 12 | List(ctx context.Context, offset, limit int64) ([]domain.ModelGroup, int64, error) 13 | Delete(ctx context.Context, id int64) (int64, error) 14 | } 15 | 16 | type groupService struct { 17 | repo repository.MGRepository 18 | } 19 | 20 | func NewMGService(repo repository.MGRepository) MGService { 21 | return &groupService{ 22 | repo: repo, 23 | } 24 | } 25 | 26 | func (s *groupService) List(ctx context.Context, offset, limit int64) ([]domain.ModelGroup, int64, error) { 27 | var ( 28 | total int64 29 | mgs []domain.ModelGroup 30 | eg errgroup.Group 31 | ) 32 | eg.Go(func() error { 33 | var err error 34 | mgs, err = s.repo.List(ctx, offset, limit) 35 | return err 36 | }) 37 | eg.Go(func() error { 38 | var err error 39 | total, err = s.repo.Total(ctx) 40 | return err 41 | }) 42 | if err := eg.Wait(); err != nil { 43 | return mgs, total, err 44 | } 45 | return mgs, total, nil 46 | } 47 | 48 | func (s *groupService) Create(ctx context.Context, req domain.ModelGroup) (int64, error) { 49 | return s.repo.CreateModelGroup(ctx, req) 50 | } 51 | 52 | func (s *groupService) Delete(ctx context.Context, id int64) (int64, error) { 53 | return s.repo.DeleteModelGroup(ctx, id) 54 | } 55 | -------------------------------------------------------------------------------- /internal/model/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/model/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | modelRelationIsNotFountResult = ginx.Result{ 15 | Code: errs.RelationIsNotFountResult.Code, 16 | Msg: errs.RelationIsNotFountResult.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/model/internal/web/retrieve.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/model/internal/domain" 5 | "github.com/ecodeclub/ekit/slice" 6 | ) 7 | 8 | type ModelListByGroupId struct { 9 | GroupId int64 `json:"group_id"` 10 | GroupName string `json:"group_name"` 11 | Models []Model `json:"models"` 12 | } 13 | 14 | type RetrieveModelListByGroupId struct { 15 | Mgs []ModelListByGroupId `json:"mgs"` 16 | } 17 | 18 | // 组合前端数据 19 | func groupModelsByGroupId(models []domain.Model, resourceCount map[string]int) map[int64][]Model { 20 | mds := make(map[int64][]Model) 21 | 22 | for _, m := range models { 23 | // 计算模型资源数量 24 | count := resourceCount[m.UID] 25 | 26 | // 构建 Model 27 | model := Model{ 28 | Id: m.ID, 29 | Name: m.Name, 30 | Total: count, 31 | UID: m.UID, 32 | Icon: m.Icon, 33 | } 34 | 35 | // 按 GroupId 分组 36 | mds[m.GroupId] = append(mds[m.GroupId], model) 37 | } 38 | 39 | return mds 40 | } 41 | 42 | // 前端展示 43 | func retrieveModelListByGroupId(models []domain.Model, modelGroups []domain.ModelGroup, resourceCount map[string]int) []ModelListByGroupId { 44 | mds := groupModelsByGroupId(models, resourceCount) 45 | return slice.Map(modelGroups, func(idx int, src domain.ModelGroup) ModelListByGroupId { 46 | return ModelListByGroupId{ 47 | GroupId: src.ID, 48 | GroupName: src.Name, 49 | Models: mds[src.ID], 50 | } 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /internal/model/module.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Module struct { 4 | Svc Service 5 | Hdl *Handler 6 | } 7 | -------------------------------------------------------------------------------- /internal/model/type.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/model/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/model/internal/web" 6 | ) 7 | 8 | type Handler = web.Handler 9 | 10 | type Service = service.Service 11 | -------------------------------------------------------------------------------- /internal/model/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package model 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/Duke1616/ecmdb/internal/model/internal/repository" 8 | "github.com/Duke1616/ecmdb/internal/model/internal/repository/dao" 9 | "github.com/Duke1616/ecmdb/internal/model/internal/service" 10 | "github.com/Duke1616/ecmdb/internal/model/internal/web" 11 | "github.com/Duke1616/ecmdb/internal/relation" 12 | "github.com/Duke1616/ecmdb/internal/resource" 13 | "github.com/Duke1616/ecmdb/pkg/mongox" 14 | "github.com/google/wire" 15 | ) 16 | 17 | var ProviderSet = wire.NewSet( 18 | web.NewHandler, 19 | initMGProvider, 20 | initModelProvider) 21 | 22 | func InitModule(db *mongox.Mongo, rmModule *relation.Module, attrModule *attribute.Module, resourceSvc *resource.Module) (*Module, error) { 23 | wire.Build( 24 | ProviderSet, 25 | wire.FieldsOf(new(*relation.Module), "RMSvc"), 26 | wire.FieldsOf(new(*attribute.Module), "Svc"), 27 | wire.FieldsOf(new(*resource.Module), "Svc"), 28 | wire.Struct(new(Module), "*"), 29 | ) 30 | return new(Module), nil 31 | } 32 | 33 | var initMGProvider = wire.NewSet( 34 | service.NewMGService, 35 | repository.NewMGRepository, 36 | dao.NewModelGroupDAO, 37 | ) 38 | 39 | var initModelProvider = wire.NewSet( 40 | service.NewModelService, 41 | repository.NewModelRepository, 42 | dao.NewModelDAO) 43 | -------------------------------------------------------------------------------- /internal/order/internal/domain/order.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Status uint8 4 | 5 | func (s Status) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | func (s Status) ToInt() int { 10 | return int(s) 11 | } 12 | 13 | const ( 14 | // START 等待开始 15 | START Status = 1 16 | // PROCESS 流程运行中 17 | PROCESS Status = 2 18 | // END 完成 19 | END Status = 3 20 | // WITHDRAW 撤回 21 | WITHDRAW Status = 4 22 | ) 23 | 24 | type Provide uint8 25 | 26 | func (s Provide) ToUint8() uint8 { 27 | return uint8(s) 28 | } 29 | 30 | const ( 31 | // SYSTEM 本系统 32 | SYSTEM Provide = 1 33 | // WECHAT 企业微信 34 | WECHAT Provide = 2 35 | ) 36 | 37 | type Order struct { 38 | Id int64 39 | TemplateId int64 40 | TemplateName string 41 | WorkflowId int64 42 | Data map[string]interface{} 43 | Status Status 44 | Provide Provide 45 | CreateBy string 46 | Process Process 47 | Ctime int64 48 | Wtime int64 49 | } 50 | 51 | type Process struct { 52 | InstanceId int 53 | } 54 | -------------------------------------------------------------------------------- /internal/order/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ValidationError = ErrorCode{Code: 503002, Msg: "验证错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/order/internal/event/producer.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mqx" 6 | "github.com/ecodeclub/mq-api" 7 | ) 8 | 9 | type CreateProcessEventProducer interface { 10 | Produce(ctx context.Context, evt OrderEvent) error 11 | } 12 | 13 | func NewCreateProcessEventProducer(q mq.MQ) (CreateProcessEventProducer, error) { 14 | return mqx.NewGeneralProducer[OrderEvent](q, CreateProcessEventName) 15 | } 16 | -------------------------------------------------------------------------------- /internal/order/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/order/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | validateErrorResult = ginx.Result{ 15 | Code: errs.ValidationError.Code, 16 | Msg: errs.ValidationError.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/order/module.go: -------------------------------------------------------------------------------- 1 | package order 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/order/internal/event/consumer" 5 | "github.com/Duke1616/ecmdb/internal/order/internal/web" 6 | ) 7 | 8 | type Module struct { 9 | Hdl *web.Handler 10 | Svc Service 11 | cw *consumer.WechatOrderConsumer 12 | cs *consumer.ProcessEventConsumer 13 | cms *consumer.OrderStatusModifyEventConsumer 14 | cf *consumer.FeishuCallbackEventConsumer 15 | } 16 | -------------------------------------------------------------------------------- /internal/order/types.go: -------------------------------------------------------------------------------- 1 | package order 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/order/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/order/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/order/internal/web" 7 | ) 8 | 9 | type Service = service.Service 10 | 11 | type Handler = web.Handler 12 | 13 | const ( 14 | EndProcess = domain.END 15 | SystemProvide = domain.SYSTEM 16 | WechatProvide = domain.WECHAT 17 | ) 18 | 19 | type Order = domain.Order 20 | -------------------------------------------------------------------------------- /internal/permission/REDEME.md: -------------------------------------------------------------------------------- 1 | # 组合权限模块 -------------------------------------------------------------------------------- /internal/permission/internal/domain/permission.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Action uint8 4 | 5 | func (s Action) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // CREATE 创建动作,比如 ADMIN 超级管理员自动权限录入 11 | // TODO 拥有根菜单权限录入 12 | CREATE Action = 1 13 | // WRITE 写入动作 14 | WRITE Action = 2 15 | // REWRITE 全部删除、重新录入数据 16 | REWRITE Action = 3 17 | ) 18 | 19 | type MenuEvent struct { 20 | Menu Menu `json:"menu"` 21 | Action Action `json:"action"` 22 | } 23 | 24 | type Menu struct { 25 | Id int64 `json:"id"` 26 | Endpoints []Endpoint `json:"endpoints"` 27 | } 28 | 29 | type Endpoint struct { 30 | Path string `json:"path"` 31 | Method string `json:"method"` 32 | } 33 | -------------------------------------------------------------------------------- /internal/permission/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/permission/internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | const ( 4 | // MenuChangeEventName 接收企业微信 OA 事件 5 | MenuChangeEventName = "menu_change_events" 6 | ) 7 | -------------------------------------------------------------------------------- /internal/permission/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/permission/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/permission/model.go: -------------------------------------------------------------------------------- 1 | package permission 2 | 3 | import "github.com/Duke1616/ecmdb/internal/permission/internal/event" 4 | 5 | type Module struct { 6 | Hdl *Handler 7 | c *event.MenuChangeEventConsumer 8 | } 9 | -------------------------------------------------------------------------------- /internal/permission/types.go: -------------------------------------------------------------------------------- 1 | package permission 2 | 3 | import "github.com/Duke1616/ecmdb/internal/permission/internal/web" 4 | 5 | type Handler = web.Handler 6 | -------------------------------------------------------------------------------- /internal/permission/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package permission 4 | 5 | import ( 6 | "context" 7 | "github.com/Duke1616/ecmdb/internal/menu" 8 | "github.com/Duke1616/ecmdb/internal/permission/internal/event" 9 | "github.com/Duke1616/ecmdb/internal/permission/internal/service" 10 | "github.com/Duke1616/ecmdb/internal/permission/internal/web" 11 | "github.com/Duke1616/ecmdb/internal/policy" 12 | "github.com/Duke1616/ecmdb/internal/role" 13 | "github.com/Duke1616/ecmdb/pkg/mongox" 14 | "github.com/ecodeclub/mq-api" 15 | "github.com/google/wire" 16 | ) 17 | 18 | func InitModule(db *mongox.Mongo, q mq.MQ, roleModule *role.Module, menuModule *menu.Module, policyModule *policy.Module) (*Module, error) { 19 | wire.Build( 20 | web.NewHandler, 21 | service.NewService, 22 | InitMenuChangeEventConsumer, 23 | wire.Struct(new(Module), "*"), 24 | wire.FieldsOf(new(*menu.Module), "Svc"), 25 | wire.FieldsOf(new(*role.Module), "Svc"), 26 | wire.FieldsOf(new(*policy.Module), "Svc"), 27 | ) 28 | return new(Module), nil 29 | } 30 | 31 | func InitMenuChangeEventConsumer(q mq.MQ, svc service.Service) *event.MenuChangeEventConsumer { 32 | c, err := event.NewMenuChangeEventConsumer(q, svc) 33 | if err != nil { 34 | return nil 35 | } 36 | 37 | c.Start(context.Background()) 38 | return c 39 | } 40 | -------------------------------------------------------------------------------- /internal/pkg/middleware/check_policy_builder.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/policy" 5 | "github.com/ecodeclub/ginx" 6 | "github.com/ecodeclub/ginx/session" 7 | "github.com/gin-gonic/gin" 8 | "github.com/gotomicro/ego/core/elog" 9 | "net/http" 10 | "strconv" 11 | ) 12 | 13 | type CheckPolicyMiddlewareBuilder struct { 14 | svc policy.Service 15 | logger *elog.Component 16 | sp session.Provider 17 | } 18 | 19 | func NewCheckPolicyMiddlewareBuilder(svc policy.Service) *CheckPolicyMiddlewareBuilder { 20 | return &CheckPolicyMiddlewareBuilder{ 21 | svc: svc, 22 | logger: elog.DefaultLogger, 23 | } 24 | } 25 | 26 | func (c *CheckPolicyMiddlewareBuilder) Build() gin.HandlerFunc { 27 | if c.sp == nil { 28 | c.sp = session.DefaultProvider() 29 | } 30 | return func(ctx *gin.Context) { 31 | gCtx := &ginx.Context{Context: ctx} 32 | sess, err := c.sp.Get(gCtx) 33 | if err != nil { 34 | gCtx.AbortWithStatus(http.StatusForbidden) 35 | c.logger.Debug("用户未登录", elog.FieldErr(err)) 36 | return 37 | } 38 | 39 | // 获取请求的路径 40 | path := ctx.Request.URL.Path 41 | // 获取请求的HTTP方法 42 | method := ctx.Request.Method 43 | // 获取用户ID 44 | uid := sess.Claims().Uid 45 | ok, err := c.svc.Authorize(ctx.Request.Context(), strconv.FormatInt(uid, 10), path, method) 46 | if err != nil || !ok { 47 | gCtx.AbortWithStatus(http.StatusForbidden) 48 | c.logger.Debug("用户无权限", elog.FieldErr(err)) 49 | return 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/pkg/rule/rule_test.go: -------------------------------------------------------------------------------- 1 | package rule 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/stretchr/testify/assert" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | func TestRule(t *testing.T) { 11 | // 读取 JSON 文件 12 | data, err := ioutil.ReadFile("rule.json") 13 | assert.NoError(t, err) 14 | 15 | // 解析数据 16 | var rules interface{} 17 | err = json.Unmarshal(data, &rules) 18 | assert.NoError(t, err) 19 | 20 | result, err := ParseRules(rules) 21 | assert.NoError(t, err) 22 | 23 | assert.Len(t, result, 5) 24 | } 25 | 26 | func TestField(t *testing.T) { 27 | // 读取 JSON 文件 28 | data, err := ioutil.ReadFile("rule.json") 29 | assert.NoError(t, err) 30 | 31 | // 解析数据 32 | var rules interface{} 33 | err = json.Unmarshal(data, &rules) 34 | assert.NoError(t, err) 35 | 36 | result, err := ParseRules(rules) 37 | assert.NoError(t, err) 38 | assert.Len(t, result, 5) 39 | 40 | card := GetFields(result, 1, map[string]interface{}{ 41 | "assets": []string{"7601628b-3567-472e-af13-4f2c6ad631c4", "b6d7171f-84b1-4778-9f9c-0960cb7b1d42"}, 42 | "environment": "internal", 43 | "purpose": "为了荣耀", 44 | "quantity": 2, 45 | "role": 1, 46 | }) 47 | 48 | print(card) 49 | } 50 | -------------------------------------------------------------------------------- /internal/pkg/rule/title.go: -------------------------------------------------------------------------------- 1 | package rule 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func GenerateTitle(displayName string, templateName string) string { 9 | if strings.HasSuffix(templateName, "申请") { 10 | return fmt.Sprintf("%s的%s", displayName, templateName) 11 | } 12 | return fmt.Sprintf("%s的%s申请", displayName, templateName) 13 | } 14 | 15 | func GenerateCCTitle(displayName string, templateName string) string { 16 | if strings.HasSuffix(templateName, "申请") { 17 | return fmt.Sprintf("%s抄送了%s给你", displayName, templateName) 18 | } 19 | return fmt.Sprintf("%s抄送了%s申请给你", displayName, templateName) 20 | } 21 | 22 | func GenerateAutoTitle(displayName string, templateName string) string { 23 | title := GenerateTitle(displayName, templateName) 24 | return fmt.Sprintf("%s自动化任务结果", title) 25 | } 26 | -------------------------------------------------------------------------------- /internal/pkg/wechat/marshal.go: -------------------------------------------------------------------------------- 1 | package wechat 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/xen0n/go-workwx" 7 | ) 8 | 9 | func Marshal(oaDetail workwx.OAApprovalDetail) (map[string]interface{}, error) { 10 | evtData, err := json.Marshal(oaDetail) 11 | if err != nil { 12 | fmt.Println("Error marshalling struct:", err) 13 | } 14 | 15 | var data map[string]interface{} 16 | err = json.Unmarshal(evtData, &data) 17 | return data, err 18 | } 19 | 20 | func Unmarshal(data map[string]interface{}) (workwx.OAApprovalDetail, error) { 21 | evtData, err := json.Marshal(data) 22 | if err != nil { 23 | return workwx.OAApprovalDetail{}, err 24 | } 25 | 26 | var oaDetail workwx.OAApprovalDetail 27 | err = json.Unmarshal(evtData, &oaDetail) 28 | return oaDetail, err 29 | } 30 | -------------------------------------------------------------------------------- /internal/policy/internal/domain/policy.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Effect string 4 | 5 | func (s Effect) ToString() string { 6 | return string(s) 7 | } 8 | 9 | const ( 10 | // ALLOW 同意 11 | ALLOW Effect = "allow" 12 | // DENY 拒绝 13 | DENY Effect = "deny" 14 | ) 15 | 16 | type Policies struct { 17 | RoleCode string 18 | Policies []Policy 19 | } 20 | 21 | type Policy struct { 22 | Path string 23 | Method string 24 | Effect Effect 25 | } 26 | 27 | type AddGroupingPolicy struct { 28 | UserId string 29 | RoleCode string 30 | } 31 | 32 | type GroupingPolicyReq struct { 33 | UserId string 34 | RoleCode string 35 | } 36 | -------------------------------------------------------------------------------- /internal/policy/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/policy/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/policy/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/policy/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type Effect string 4 | 5 | const ( 6 | // ALLOW 同意 7 | ALLOW Effect = "allow" 8 | // DENY 拒绝 9 | DENY Effect = "deny" 10 | ) 11 | 12 | type PolicyReq struct { 13 | RoleCode string `json:"role_code"` 14 | Policies []Policy `json:"policies"` 15 | } 16 | 17 | type Policy struct { 18 | Path string `json:"path"` 19 | Method string `json:"method"` 20 | Effect Effect `json:"effect"` 21 | } 22 | 23 | type AddGroupingPolicyReq struct { 24 | UserId string `json:"user_id"` 25 | RoleCode string `json:"role_code"` 26 | } 27 | 28 | type GetPermissionsForUserReq struct { 29 | UserId int64 `json:"user_id"` 30 | } 31 | 32 | type GetPermissionsForRoleReq struct { 33 | RoleCode string `json:"role_code"` 34 | } 35 | 36 | type AuthorizeReq struct { 37 | UserId string `json:"user_id"` 38 | Path string `json:"path"` 39 | Method string `json:"method"` 40 | } 41 | 42 | type RetrievePolicies struct { 43 | Policies []Policy `json:"policies"` 44 | } 45 | -------------------------------------------------------------------------------- /internal/policy/module.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | Svc Service 6 | } 7 | -------------------------------------------------------------------------------- /internal/policy/types.go: -------------------------------------------------------------------------------- 1 | package policy 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/policy/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/policy/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/policy/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Policy = domain.Policy 14 | 15 | type Policies = domain.Policies 16 | -------------------------------------------------------------------------------- /internal/policy/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package policy 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/policy/internal/service" 7 | "github.com/Duke1616/ecmdb/internal/policy/internal/web" 8 | "github.com/casbin/casbin/v2" 9 | "github.com/google/wire" 10 | ) 11 | 12 | var ProviderSet = wire.NewSet( 13 | web.NewHandler, 14 | service.NewService, 15 | ) 16 | 17 | func InitModule(enforcer *casbin.SyncedEnforcer) (*Module, error) { 18 | wire.Build( 19 | ProviderSet, 20 | wire.Struct(new(Module), "*"), 21 | ) 22 | return new(Module), nil 23 | } 24 | -------------------------------------------------------------------------------- /internal/policy/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package policy 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/policy/internal/service" 11 | "github.com/Duke1616/ecmdb/internal/policy/internal/web" 12 | "github.com/casbin/casbin/v2" 13 | "github.com/google/wire" 14 | ) 15 | 16 | // Injectors from wire.go: 17 | 18 | func InitModule(enforcer *casbin.SyncedEnforcer) (*Module, error) { 19 | serviceService := service.NewService(enforcer) 20 | handler := web.NewHandler(serviceService) 21 | module := &Module{ 22 | Hdl: handler, 23 | Svc: serviceService, 24 | } 25 | return module, nil 26 | } 27 | 28 | // wire.go: 29 | 30 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService) 31 | -------------------------------------------------------------------------------- /internal/relation/REDEME.md: -------------------------------------------------------------------------------- 1 | # 关联关系 2 | ## 关联关系类型 3 | - 从属关系 4 | - 连接关系 5 | - 1-1 6 | - 1-N 7 | - N-N 8 | 9 | ## 模型之间关联关系 10 | 11 | ## 资产数据进行关联 -------------------------------------------------------------------------------- /internal/relation/internal/domain/realtion_resource.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type ResourceRelation struct { 4 | ID int64 5 | SourceModelUID string 6 | TargetModelUID string 7 | SourceResourceID int64 8 | TargetResourceID int64 9 | RelationTypeUID string // 关联类型唯一索引 10 | RelationName string // 拼接字符 11 | } 12 | 13 | type ResourceAggregatedAssets struct { 14 | RelationName string 15 | ModelUid string 16 | Total int 17 | ResourceIds []int64 18 | } 19 | 20 | type ResourceDiagram struct { 21 | SRC []ResourceRelation 22 | DST []ResourceRelation 23 | } 24 | -------------------------------------------------------------------------------- /internal/relation/internal/domain/relation.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | MappingOneToOne = iota + 1 // 一对一关系 9 | MappingOneToMany // 一对多关系 10 | MappingManyToMany // 多对多关系 11 | ) 12 | 13 | type ModelRelation struct { 14 | ID int64 15 | SourceModelUID string 16 | TargetModelUID string 17 | RelationTypeUID string // 关联类型唯一索引 18 | RelationName string // 拼接字符 19 | Mapping string // 关联关系 20 | Ctime time.Time 21 | Utime time.Time 22 | } 23 | 24 | // ModelDiagram 拓补图模型关联节点信息 25 | type ModelDiagram struct { 26 | ID int64 27 | RelationTypeUid string 28 | TargetModelUid string 29 | SourceModelUid string 30 | } 31 | 32 | type RelationType struct { 33 | ID int64 34 | Name string 35 | UID string 36 | SourceDescribe string 37 | TargetDescribe string 38 | Ctime time.Time 39 | Utime time.Time 40 | } 41 | -------------------------------------------------------------------------------- /internal/relation/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 504001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | 12 | func (e ErrorCode) Error() string { 13 | //TODO implement me 14 | panic("implement me") 15 | } 16 | -------------------------------------------------------------------------------- /internal/relation/internal/integration/startup/db.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | "log" 9 | "time" 10 | ) 11 | 12 | func InitMongoDB() *mongox.Mongo { 13 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 14 | defer cancel() 15 | 16 | opts := options.Client(). 17 | ApplyURI("mongodb://cmdb:123456@10.31.0.200:47017/cmdb") 18 | client, err := mongo.Connect(ctx, opts) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | if err = client.Ping(ctx, nil); err != nil { 25 | log.Panicf("ping mongodb server error, %s", err) 26 | } 27 | 28 | return mongox.NewMongo(client, "cmdb-e2e") 29 | } 30 | -------------------------------------------------------------------------------- /internal/relation/internal/integration/startup/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package startup 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/relation" 7 | "github.com/google/wire" 8 | ) 9 | 10 | func InitRMHandler() (*relation.RMHandler, error) { 11 | wire.Build(InitMongoDB, 12 | relation.InitModule, 13 | wire.FieldsOf(new(*relation.Module), "RMHdl"), 14 | ) 15 | return new(relation.RMHandler), nil 16 | } 17 | 18 | func InitRRHandler() (*relation.RRHandler, error) { 19 | wire.Build(InitMongoDB, 20 | relation.InitModule, 21 | wire.FieldsOf(new(*relation.Module), "RRHdl"), 22 | ) 23 | return new(relation.RRHandler), nil 24 | } 25 | 26 | func InitRRSvc() relation.RRSvc { 27 | wire.Build(InitMongoDB, relation.InitRRService) 28 | return nil 29 | } 30 | 31 | func InitRMSvc() relation.RMSvc { 32 | wire.Build(InitMongoDB, relation.InitRMService) 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/relation/internal/integration/startup/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package startup 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/relation" 11 | "github.com/Duke1616/ecmdb/internal/relation/internal/service" 12 | "github.com/Duke1616/ecmdb/internal/relation/internal/web" 13 | ) 14 | 15 | // Injectors from wire.go: 16 | 17 | func InitRMHandler() (*web.RelationModelHandler, error) { 18 | mongo := InitMongoDB() 19 | module, err := relation.InitModule(mongo) 20 | if err != nil { 21 | return nil, err 22 | } 23 | relationModelHandler := module.RMHdl 24 | return relationModelHandler, nil 25 | } 26 | 27 | func InitRRHandler() (*web.RelationResourceHandler, error) { 28 | mongo := InitMongoDB() 29 | module, err := relation.InitModule(mongo) 30 | if err != nil { 31 | return nil, err 32 | } 33 | relationResourceHandler := module.RRHdl 34 | return relationResourceHandler, nil 35 | } 36 | 37 | func InitRRSvc() service.RelationResourceService { 38 | mongo := InitMongoDB() 39 | relationResourceService := relation.InitRRService(mongo) 40 | return relationResourceService 41 | } 42 | 43 | func InitRMSvc() service.RelationModelService { 44 | mongo := InitMongoDB() 45 | relationModelService := relation.InitRMService(mongo) 46 | return relationModelService 47 | } 48 | -------------------------------------------------------------------------------- /internal/relation/internal/repository/dao/type.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | const ( 4 | ModelRelationCollection = "c_relation_model" 5 | ResourceRelationCollection = "c_relation_resource" 6 | RelationTypeCollection = "c_relation_type" 7 | ) 8 | -------------------------------------------------------------------------------- /internal/relation/internal/service/relation_type.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/relation/internal/domain" 6 | "github.com/Duke1616/ecmdb/internal/relation/internal/repository" 7 | "golang.org/x/sync/errgroup" 8 | ) 9 | 10 | type RelationTypeService interface { 11 | Create(ctx context.Context, req domain.RelationType) (int64, error) 12 | List(ctx context.Context, offset, limit int64) ([]domain.RelationType, int64, error) 13 | } 14 | 15 | type service struct { 16 | repo repository.RelationTypeRepository 17 | } 18 | 19 | func NewRelationTypeService(repo repository.RelationTypeRepository) RelationTypeService { 20 | return &service{ 21 | repo: repo, 22 | } 23 | } 24 | 25 | func (s *service) Create(ctx context.Context, req domain.RelationType) (int64, error) { 26 | return s.repo.Create(ctx, req) 27 | } 28 | 29 | func (s *service) List(ctx context.Context, offset, limit int64) ([]domain.RelationType, int64, error) { 30 | var ( 31 | eg errgroup.Group 32 | rts []domain.RelationType 33 | total int64 34 | ) 35 | eg.Go(func() error { 36 | var err error 37 | rts, err = s.repo.List(ctx, offset, limit) 38 | return err 39 | }) 40 | 41 | eg.Go(func() error { 42 | var err error 43 | total, err = s.repo.Total(ctx) 44 | return err 45 | }) 46 | if err := eg.Wait(); err != nil { 47 | return rts, total, err 48 | } 49 | return rts, total, nil 50 | } 51 | -------------------------------------------------------------------------------- /internal/relation/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/relation/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/relation/internal/web/type.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type RelationType struct { 4 | ID int64 `json:"id"` 5 | Name string `json:"name"` 6 | UID string `json:"uid"` 7 | SourceDescribe string `json:"source_describe"` 8 | TargetDescribe string `json:"target_describe"` 9 | } 10 | 11 | type ModelRelation struct { 12 | ID int64 `json:"id"` 13 | SourceModelUID string `json:"source_model_uid"` 14 | TargetModelUID string `json:"target_model_uid"` 15 | RelationTypeUID string `json:"relation_type_uid"` 16 | RelationName string `json:"relation_name"` 17 | Mapping string `json:"mapping"` 18 | } 19 | 20 | type ResourceRelation struct { 21 | ID int64 `json:"id"` 22 | SourceModelUID string `json:"source_model_uid"` 23 | TargetModelUID string `json:"target_model_uid"` 24 | SourceResourceID int64 `json:"source_resource_id"` 25 | TargetResourceID int64 `json:"target_resource_id"` 26 | RelationTypeUID string `json:"relation_type_uid"` 27 | RelationName string `json:"relation_name"` 28 | } 29 | -------------------------------------------------------------------------------- /internal/relation/module.go: -------------------------------------------------------------------------------- 1 | package relation 2 | 3 | type Module struct { 4 | RRSvc RRSvc 5 | RMSvc RMSvc 6 | RTSvc RTSvc 7 | RRHdl *RRHandler 8 | RMHdl *RMHandler 9 | RTHdl *RTHandler 10 | } 11 | -------------------------------------------------------------------------------- /internal/relation/type.go: -------------------------------------------------------------------------------- 1 | package relation 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/relation/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/relation/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/relation/internal/web" 7 | ) 8 | 9 | // RR => RelationResource 10 | // RM => RelationModel 11 | // RT => RelationType 12 | 13 | type RRSvc = service.RelationResourceService 14 | type RRHandler = web.RelationResourceHandler 15 | 16 | type RMSvc = service.RelationModelService 17 | type RMHandler = web.RelationModelHandler 18 | 19 | type RTSvc = service.RelationTypeService 20 | type RTHandler = web.RelationTypeHandler 21 | 22 | type ModelDiagram = domain.ModelDiagram 23 | 24 | type ResourceRelation = domain.ResourceRelation 25 | -------------------------------------------------------------------------------- /internal/resource/internal/domain/resource.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/Duke1616/ecmdb/pkg/mongox" 4 | 5 | type Resource struct { 6 | ID int64 `json:"id"` 7 | Name string `json:"name"` 8 | ModelUID string `json:"model_uid"` 9 | Data mongox.MapStr `json:"data"` 10 | } 11 | 12 | type ResourceRelation struct { 13 | ModelUid string 14 | Resources []Resource 15 | } 16 | 17 | type SearchResource struct { 18 | ModelUid string 19 | Total int `json:"total"` 20 | Data []mongox.MapStr 21 | } 22 | 23 | type Condition struct { 24 | Name string `json:"name"` // 过滤名称 25 | Condition string `json:"condition"` // 过滤条件 26 | Input string `json:"input"` // 过滤输入 27 | } 28 | -------------------------------------------------------------------------------- /internal/resource/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | UrlPathError = ErrorCode{Code: 503001, Msg: "URL PATH 传递错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/resource/internal/integration/startup/db.go: -------------------------------------------------------------------------------- 1 | package startup 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | "log" 9 | "time" 10 | ) 11 | 12 | func InitMongoDB() *mongox.Mongo { 13 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 14 | defer cancel() 15 | 16 | opts := options.Client(). 17 | ApplyURI("mongodb://cmdb:123456@10.31.0.200:47017/cmdb") 18 | client, err := mongo.Connect(ctx, opts) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | if err = client.Ping(ctx, nil); err != nil { 25 | log.Panicf("ping mongodb server error, %s", err) 26 | } 27 | 28 | return mongox.NewMongo(client, "cmdb-e2e") 29 | } 30 | -------------------------------------------------------------------------------- /internal/resource/internal/integration/startup/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package startup 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/Duke1616/ecmdb/internal/relation" 8 | "github.com/Duke1616/ecmdb/internal/resource" 9 | "github.com/google/wire" 10 | ) 11 | 12 | func InitHandler(attributeModule *attribute.Module, relationModule *relation.Module) (*resource.Handler, error) { 13 | wire.Build(InitMongoDB, 14 | resource.InitModule, 15 | wire.FieldsOf(new(*resource.Module), "Hdl"), 16 | ) 17 | return new(resource.Handler), nil 18 | } 19 | -------------------------------------------------------------------------------- /internal/resource/internal/integration/startup/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package startup 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/attribute" 11 | "github.com/Duke1616/ecmdb/internal/relation" 12 | "github.com/Duke1616/ecmdb/internal/resource" 13 | "github.com/Duke1616/ecmdb/internal/resource/internal/web" 14 | ) 15 | 16 | // Injectors from wire.go: 17 | 18 | func InitHandler(attributeModule *attribute.Module, relationModule *relation.Module) (*web.Handler, error) { 19 | mongo := InitMongoDB() 20 | module, err := resource.InitModule(mongo, attributeModule, relationModule) 21 | if err != nil { 22 | return nil, err 23 | } 24 | handler := module.Hdl 25 | return handler, nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/resource/internal/repository/dao/init.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | func InitIndexes(db *mongox.Mongo) error { 12 | col := db.Collection(ResourceCollection) 13 | 14 | indexes := []mongo.IndexModel{ 15 | { 16 | Keys: bson.D{ 17 | {"name", -1}, 18 | {"model_uid", -1}, 19 | }, 20 | Options: options.Index().SetUnique(true), 21 | }, 22 | // 使用 percona mongo 创建全文检索,ngram 进行分词 23 | { 24 | Keys: bson.D{{Key: "$**", Value: "text"}}, 25 | Options: options.Index().SetDefaultLanguage("ngram"), 26 | }, 27 | } 28 | 29 | _, err := col.Indexes().CreateMany(context.Background(), indexes) 30 | 31 | return err 32 | } 33 | -------------------------------------------------------------------------------- /internal/resource/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/resource/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/resource/module.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | type Module struct { 4 | Svc Service 5 | Hdl *Handler 6 | } 7 | -------------------------------------------------------------------------------- /internal/resource/types.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/resource/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/resource/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/resource/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Resource = domain.Resource 14 | -------------------------------------------------------------------------------- /internal/resource/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package resource 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/Duke1616/ecmdb/internal/relation" 8 | "github.com/Duke1616/ecmdb/internal/resource/internal/repository" 9 | "github.com/Duke1616/ecmdb/internal/resource/internal/repository/dao" 10 | "github.com/Duke1616/ecmdb/internal/resource/internal/service" 11 | "github.com/Duke1616/ecmdb/internal/resource/internal/web" 12 | "github.com/Duke1616/ecmdb/pkg/mongox" 13 | "github.com/google/wire" 14 | "sync" 15 | ) 16 | 17 | var ProviderSet = wire.NewSet( 18 | web.NewHandler, 19 | repository.NewResourceRepository) 20 | 21 | func InitModule(db *mongox.Mongo, attributeModule *attribute.Module, relationModule *relation.Module) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | NewService, 25 | InitResourceDAO, 26 | wire.FieldsOf(new(*attribute.Module), "Svc"), 27 | wire.FieldsOf(new(*relation.Module), "RRSvc"), 28 | wire.Struct(new(Module), "*"), 29 | ) 30 | return new(Module), nil 31 | } 32 | 33 | var daoOnce = sync.Once{} 34 | 35 | func InitCollectionOnce(db *mongox.Mongo) { 36 | daoOnce.Do(func() { 37 | err := dao.InitIndexes(db) 38 | if err != nil { 39 | panic(err) 40 | } 41 | }) 42 | } 43 | 44 | func InitResourceDAO(db *mongox.Mongo) dao.ResourceDAO { 45 | InitCollectionOnce(db) 46 | return dao.NewResourceDAO(db) 47 | } 48 | 49 | func NewService(repo repository.ResourceRepository) Service { 50 | return service.NewService(repo) 51 | } 52 | -------------------------------------------------------------------------------- /internal/role/internal/domain/role.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Role struct { 4 | Id int64 5 | Code string 6 | Name string 7 | Desc string 8 | Status bool 9 | MenuIds []int64 10 | } 11 | -------------------------------------------------------------------------------- /internal/role/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/role/internal/repository/dao/init.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | func InitIndexes(db *mongox.Mongo) error { 12 | col := db.Collection(RoleCollection) 13 | 14 | indexes := []mongo.IndexModel{ 15 | { 16 | Keys: bson.D{ 17 | {"code", -1}, 18 | }, 19 | Options: options.Index().SetUnique(true), 20 | }, 21 | } 22 | 23 | _, err := col.Indexes().CreateMany(context.Background(), indexes) 24 | return err 25 | } 26 | -------------------------------------------------------------------------------- /internal/role/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/role/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/role/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type CreateRoleReq struct { 4 | Name string `json:"name"` 5 | Code string `json:"code"` 6 | Desc string `json:"desc"` 7 | Status bool `json:"status"` 8 | } 9 | 10 | type DeleteRoleReq struct { 11 | Id int64 `json:"id"` 12 | } 13 | 14 | type UpdateRoleReq struct { 15 | Id int64 `json:"id"` 16 | Name string `json:"name"` 17 | Code string `json:"code"` 18 | Desc string `json:"desc"` 19 | Status bool `json:"status"` 20 | } 21 | 22 | type Role struct { 23 | Id int64 `json:"id"` 24 | Name string `json:"name"` 25 | Code string `json:"code"` 26 | Desc string `json:"desc"` 27 | Status bool `json:"status"` 28 | } 29 | 30 | type Page struct { 31 | Offset int64 `json:"offset,omitempty"` 32 | Limit int64 `json:"limit,omitempty"` 33 | } 34 | 35 | type RetrieveRoles struct { 36 | Roles []Role `json:"roles"` 37 | Total int64 `json:"total"` 38 | } 39 | 40 | type AddPermissionForRoleReq struct { 41 | RoleCode string `json:"role_code"` 42 | MenuIds []int64 `json:"menu_ids"` 43 | } 44 | 45 | type UserRole struct { 46 | Page 47 | Codes []string `json:"codes"` 48 | } 49 | 50 | type RetrieveUserDoesNotHaveRoles struct { 51 | Total int64 `json:"total"` 52 | Roles []Role `json:"roles"` 53 | } 54 | 55 | type RetrieveUserHaveRoles struct { 56 | Roles []Role `json:"roles"` 57 | } 58 | -------------------------------------------------------------------------------- /internal/role/module.go: -------------------------------------------------------------------------------- 1 | package role 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | Svc Service 6 | } 7 | -------------------------------------------------------------------------------- /internal/role/types.go: -------------------------------------------------------------------------------- 1 | package role 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/role/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/role/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/role/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Role = domain.Role 14 | -------------------------------------------------------------------------------- /internal/role/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package role 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/role/internal/repository" 7 | "github.com/Duke1616/ecmdb/internal/role/internal/repository/dao" 8 | "github.com/Duke1616/ecmdb/internal/role/internal/service" 9 | "github.com/Duke1616/ecmdb/internal/role/internal/web" 10 | "github.com/Duke1616/ecmdb/pkg/mongox" 11 | "github.com/google/wire" 12 | "sync" 13 | ) 14 | 15 | var ProviderSet = wire.NewSet( 16 | web.NewHandler, 17 | service.NewService, 18 | repository.NewRoleRepository, 19 | ) 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | wire.Build( 23 | ProviderSet, 24 | InitRoleDAO, 25 | wire.Struct(new(Module), "*"), 26 | ) 27 | return new(Module), nil 28 | } 29 | 30 | var daoOnce = sync.Once{} 31 | 32 | func InitCollectionOnce(db *mongox.Mongo) { 33 | daoOnce.Do(func() { 34 | err := dao.InitIndexes(db) 35 | if err != nil { 36 | panic(err) 37 | } 38 | }) 39 | } 40 | 41 | func InitRoleDAO(db *mongox.Mongo) dao.RoleDAO { 42 | InitCollectionOnce(db) 43 | return dao.NewRoleDAO(db) 44 | } 45 | -------------------------------------------------------------------------------- /internal/role/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package role 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/role/internal/repository" 11 | "github.com/Duke1616/ecmdb/internal/role/internal/repository/dao" 12 | "github.com/Duke1616/ecmdb/internal/role/internal/service" 13 | "github.com/Duke1616/ecmdb/internal/role/internal/web" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/google/wire" 16 | "sync" 17 | ) 18 | 19 | // Injectors from wire.go: 20 | 21 | func InitModule(db *mongox.Mongo) (*Module, error) { 22 | roleDAO := InitRoleDAO(db) 23 | roleRepository := repository.NewRoleRepository(roleDAO) 24 | serviceService := service.NewService(roleRepository) 25 | handler := web.NewHandler(serviceService) 26 | module := &Module{ 27 | Hdl: handler, 28 | Svc: serviceService, 29 | } 30 | return module, nil 31 | } 32 | 33 | // wire.go: 34 | 35 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewRoleRepository) 36 | 37 | var daoOnce = sync.Once{} 38 | 39 | func InitCollectionOnce(db *mongox.Mongo) { 40 | daoOnce.Do(func() { 41 | err := dao.InitIndexes(db) 42 | if err != nil { 43 | panic(err) 44 | } 45 | }) 46 | } 47 | 48 | func InitRoleDAO(db *mongox.Mongo) dao.RoleDAO { 49 | InitCollectionOnce(db) 50 | return dao.NewRoleDAO(db) 51 | } 52 | -------------------------------------------------------------------------------- /internal/rota/README.md: -------------------------------------------------------------------------------- 1 | # 值班管理 -------------------------------------------------------------------------------- /internal/rota/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/rota/internal/service/schedule/types.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import "github.com/Duke1616/ecmdb/internal/rota/internal/domain" 4 | 5 | type Scheduler interface { 6 | // GenerateSchedule 生成排班表 7 | GenerateSchedule(rule domain.RotaRule, adjustmentRules []domain.RotaAdjustmentRule, 8 | stime int64, etime int64) (domain.ShiftRostered, error) 9 | // GetCurrentSchedule 查询当期排班 10 | GetCurrentSchedule(rule domain.RotaRule, adjustmentRules []domain.RotaAdjustmentRule) (domain.Schedule, error) 11 | } 12 | -------------------------------------------------------------------------------- /internal/rota/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/rota/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/rota/module.go: -------------------------------------------------------------------------------- 1 | package rota 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | Svc Service 6 | } 7 | -------------------------------------------------------------------------------- /internal/rota/types.go: -------------------------------------------------------------------------------- 1 | package rota 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/rota/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/rota/internal/web" 6 | ) 7 | 8 | type Handler = web.Handler 9 | 10 | type Service = service.Service 11 | -------------------------------------------------------------------------------- /internal/rota/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package rota 4 | 5 | import ( 6 | "fmt" 7 | "github.com/Duke1616/ecmdb/internal/rota/internal/repository" 8 | "github.com/Duke1616/ecmdb/internal/rota/internal/repository/dao" 9 | "github.com/Duke1616/ecmdb/internal/rota/internal/service" 10 | "github.com/Duke1616/ecmdb/internal/rota/internal/service/schedule" 11 | "github.com/Duke1616/ecmdb/internal/rota/internal/web" 12 | "github.com/Duke1616/ecmdb/pkg/mongox" 13 | "github.com/google/wire" 14 | "time" 15 | ) 16 | 17 | var ProviderSet = wire.NewSet( 18 | web.NewHandler, 19 | service.NewService, 20 | repository.NewRotaRepository, 21 | dao.NewRotaDao, 22 | ) 23 | 24 | func InitModule(db *mongox.Mongo) (*Module, error) { 25 | wire.Build( 26 | ProviderSet, 27 | InitScheduleRule, 28 | wire.Struct(new(Module), "*"), 29 | ) 30 | return new(Module), nil 31 | } 32 | 33 | func InitScheduleRule() schedule.Scheduler { 34 | // 创建一个位置对象,表示中国北京的位置 35 | location, err := time.LoadLocation("Asia/Shanghai") 36 | if err != nil { 37 | fmt.Print() 38 | } 39 | 40 | return schedule.NewRruleSchedule(location) 41 | } 42 | -------------------------------------------------------------------------------- /internal/rota/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package rota 8 | 9 | import ( 10 | "fmt" 11 | "github.com/Duke1616/ecmdb/internal/rota/internal/repository" 12 | "github.com/Duke1616/ecmdb/internal/rota/internal/repository/dao" 13 | "github.com/Duke1616/ecmdb/internal/rota/internal/service" 14 | "github.com/Duke1616/ecmdb/internal/rota/internal/service/schedule" 15 | "github.com/Duke1616/ecmdb/internal/rota/internal/web" 16 | "github.com/Duke1616/ecmdb/pkg/mongox" 17 | "github.com/google/wire" 18 | "time" 19 | ) 20 | 21 | // Injectors from wire.go: 22 | 23 | func InitModule(db *mongox.Mongo) (*Module, error) { 24 | rotaDao := dao.NewRotaDao(db) 25 | rotaRepository := repository.NewRotaRepository(rotaDao) 26 | scheduler := InitScheduleRule() 27 | serviceService := service.NewService(rotaRepository, scheduler) 28 | handler := web.NewHandler(serviceService) 29 | module := &Module{ 30 | Hdl: handler, 31 | Svc: serviceService, 32 | } 33 | return module, nil 34 | } 35 | 36 | // wire.go: 37 | 38 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewRotaRepository, dao.NewRotaDao) 39 | 40 | func InitScheduleRule() schedule.Scheduler { 41 | 42 | location, err := time.LoadLocation("Asia/Shanghai") 43 | if err != nil { 44 | fmt.Print() 45 | } 46 | 47 | return schedule.NewRruleSchedule(location) 48 | } 49 | -------------------------------------------------------------------------------- /internal/runner/internal/domain/runner.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Action uint8 4 | 5 | func (s Action) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // REGISTER 注册 11 | REGISTER Action = 1 12 | // UNREGISTER 注销 13 | UNREGISTER Action = 2 14 | ) 15 | 16 | type Runner struct { 17 | Id int64 18 | Name string 19 | CodebookUid string 20 | CodebookSecret string 21 | WorkerName string 22 | Topic string 23 | Tags []string 24 | Desc string 25 | Action Action 26 | Variables []Variables 27 | } 28 | 29 | type Variables struct { 30 | Key string 31 | Value any 32 | Secret bool 33 | } 34 | 35 | type RunnerTags struct { 36 | CodebookUid string 37 | TagsMappingTopic map[string]string 38 | } 39 | -------------------------------------------------------------------------------- /internal/runner/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ValidationError = ErrorCode{Code: 503002, Msg: "验证错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/runner/internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | const TaskRegisterRunnerEventName = "register_runner_event" 4 | 5 | type Action uint8 6 | 7 | type TaskRunnerEvent struct { 8 | CodebookUid string 9 | CodebookSecret string 10 | WorkerName string 11 | Name string 12 | Tags []string 13 | Desc string 14 | Action Action 15 | } 16 | -------------------------------------------------------------------------------- /internal/runner/internal/repository/error.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "github.com/Duke1616/ecmdb/internal/runner/internal/repository/dao" 4 | 5 | var ErrRunnerNotFound = dao.ErrDataNotFound 6 | -------------------------------------------------------------------------------- /internal/runner/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/runner/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | validationErrorResult = ginx.Result{ 15 | Code: errs.ValidationError.Code, 16 | Msg: errs.ValidationError.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/runner/module.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/runner/internal/event" 5 | "github.com/Duke1616/ecmdb/internal/runner/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/runner/internal/web" 7 | ) 8 | 9 | type Module struct { 10 | Svc service.Service 11 | Hdl *web.Handler 12 | c *event.TaskRunnerConsumer 13 | } 14 | -------------------------------------------------------------------------------- /internal/runner/types.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/runner/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/runner/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/runner/internal/web" 7 | ) 8 | 9 | type Service = service.Service 10 | 11 | type Handler = web.Handler 12 | 13 | type Runner = domain.Runner 14 | 15 | type Variables = domain.Variables 16 | -------------------------------------------------------------------------------- /internal/strategy/internal/domain/strategy.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Strategy struct { 4 | Key string 5 | Value []string 6 | } 7 | 8 | // Condition 过滤条件 9 | type Condition struct { 10 | Key string 11 | Value string 12 | Op string 13 | } 14 | 15 | // Strategies 自动触发器规则 16 | type Strategies struct { 17 | TemplateId int64 // 模板ID 18 | CodebookIdentifier string // 绑定任务脚本 19 | RunnerTag string // 执行节点标签 20 | Conditions []Condition // 匹配条件 21 | } 22 | -------------------------------------------------------------------------------- /internal/strategy/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/strategy/internal/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/strategy/internal/domain" 6 | ) 7 | 8 | type Service interface { 9 | CreateOrder(ctx context.Context, req domain.Strategy) (int64, error) 10 | } 11 | 12 | type service struct { 13 | } 14 | 15 | func NewService() Service { 16 | return &service{} 17 | } 18 | 19 | func (s *service) CreateOrder(ctx context.Context, req domain.Strategy) (int64, error) { 20 | // TODO 匹配相应的规则, 去创建工单 21 | 22 | // TODO 去除重复 23 | panic("implement me") 24 | } 25 | -------------------------------------------------------------------------------- /internal/strategy/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/strategy/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/strategy/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type GetSpecifiedTemplate struct { 4 | Id int64 5 | } 6 | 7 | type CreateStrategyReq struct { 8 | Key string 9 | Value string 10 | Op op 11 | } 12 | 13 | // op 代表操作符 14 | type op string 15 | 16 | const ( 17 | opAND = "AND" 18 | opOR = "OR" 19 | ) 20 | 21 | func (o op) String() string { 22 | return string(o) 23 | } 24 | -------------------------------------------------------------------------------- /internal/strategy/module.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | type Module struct { 4 | Hdl *Handler 5 | } 6 | -------------------------------------------------------------------------------- /internal/strategy/types.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/strategy/internal/web" 5 | ) 6 | 7 | type Handler = web.Handler 8 | -------------------------------------------------------------------------------- /internal/strategy/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package strategy 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/strategy/internal/service" 7 | "github.com/Duke1616/ecmdb/internal/strategy/internal/web" 8 | "github.com/Duke1616/ecmdb/internal/template" 9 | "github.com/google/wire" 10 | ) 11 | 12 | var ProviderSet = wire.NewSet( 13 | web.NewHandler, 14 | service.NewService, 15 | ) 16 | 17 | func InitModule(templateModule *template.Module) (*Module, error) { 18 | wire.Build( 19 | ProviderSet, 20 | wire.FieldsOf(new(*template.Module), "Svc"), 21 | wire.Struct(new(Module), "*"), 22 | ) 23 | return new(Module), nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/strategy/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package strategy 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/strategy/internal/service" 11 | "github.com/Duke1616/ecmdb/internal/strategy/internal/web" 12 | "github.com/Duke1616/ecmdb/internal/template" 13 | "github.com/google/wire" 14 | ) 15 | 16 | // Injectors from wire.go: 17 | 18 | func InitModule(templateModule *template.Module) (*Module, error) { 19 | serviceService := service.NewService() 20 | service2 := templateModule.Svc 21 | handler := web.NewHandler(serviceService, service2) 22 | module := &Module{ 23 | Hdl: handler, 24 | } 25 | return module, nil 26 | } 27 | 28 | // wire.go: 29 | 30 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService) 31 | -------------------------------------------------------------------------------- /internal/task/README.md: -------------------------------------------------------------------------------- 1 | # 自动化执行 -------------------------------------------------------------------------------- /internal/task/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ValidationError = ErrorCode{Code: 503002, Msg: "验证错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/task/internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | const ExecuteResultEventName = "result_execute_events" 4 | 5 | type Status uint8 6 | 7 | func (s Status) ToUint8() uint8 { 8 | return uint8(s) 9 | } 10 | 11 | const ( 12 | // SUCCESS 成功 13 | SUCCESS Status = 1 14 | // FAILED 失败 15 | failed 16 | FAILED Status = 2 17 | ) 18 | 19 | type ExecuteResultEvent struct { 20 | TaskId int64 `json:"task_id"` 21 | Result string `json:"result"` 22 | WantResult string `json:"want_result"` 23 | Status Status `json:"status"` 24 | } 25 | -------------------------------------------------------------------------------- /internal/task/internal/service/cronjob.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/task/internal/domain" 6 | "github.com/gotomicro/ego/core/elog" 7 | "github.com/robfig/cron/v3" 8 | "time" 9 | ) 10 | 11 | type Cronjob interface { 12 | Create(ctx context.Context, task domain.Task) error 13 | } 14 | 15 | type cronjob struct { 16 | execService ExecService 17 | logger *elog.Component 18 | cron *cron.Cron 19 | } 20 | 21 | func NewCronjob(execService ExecService) Cronjob { 22 | return &cronjob{ 23 | execService: execService, 24 | logger: elog.DefaultLogger, 25 | cron: cron.New(cron.WithSeconds()), 26 | } 27 | } 28 | 29 | func (c *cronjob) Create(ctx context.Context, task domain.Task) error { 30 | // jobTime 秒、分、时、日、月 31 | jobTime := time.UnixMilli(task.Timing.Stime).Format("05 04 15 02 01 *") 32 | _, err := c.cron.AddFunc(jobTime, func() { 33 | if err := c.execService.Execute(ctx, task); err != nil { 34 | c.logger.Error("Task execution failed", elog.FieldErr(err)) 35 | return 36 | } 37 | c.logger.Info("task execution finished successfully", elog.Int64("task_id", task.Id)) 38 | return 39 | }) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | c.logger.Info("starting task execution", elog.Any("time", jobTime), elog.Int64("task_id", task.Id)) 45 | c.cron.Start() 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/task/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/task/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | validationErrorResult = ginx.Result{ 15 | Code: errs.ValidationError.Code, 16 | Msg: errs.ValidationError.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/task/module.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/task/internal/event" 5 | "github.com/Duke1616/ecmdb/internal/task/internal/web" 6 | ) 7 | 8 | type Module struct { 9 | Svc Service 10 | Hdl *web.Handler 11 | c *event.ExecuteResultConsumer 12 | StartTaskJob *StartTaskJob 13 | PassProcessTaskJob *PassProcessTaskJob 14 | RecoveryTaskJob *RecoveryTaskJob 15 | } 16 | -------------------------------------------------------------------------------- /internal/task/types.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/task/internal/job" 5 | "github.com/Duke1616/ecmdb/internal/task/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/task/internal/web" 7 | ) 8 | 9 | type Service = service.Service 10 | 11 | type Handler = web.Handler 12 | 13 | type StartTaskJob = job.StartTaskJob 14 | 15 | type PassProcessTaskJob = job.PassProcessTaskJob 16 | 17 | type RecoveryTaskJob = job.RecoveryTaskJob 18 | -------------------------------------------------------------------------------- /internal/template/internal/domain/domain.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/xen0n/go-workwx" 5 | ) 6 | 7 | type CreateType uint8 8 | 9 | func (s CreateType) ToUint8() uint8 { 10 | return uint8(s) 11 | } 12 | 13 | const ( 14 | // SystemCreate 系统创建 15 | SystemCreate CreateType = 1 16 | // WechatCreate 企业微信创建 OR 同步 17 | WechatCreate CreateType = 2 18 | ) 19 | 20 | type Template struct { 21 | Id int64 22 | Name string 23 | WorkflowId int64 24 | GroupId int64 25 | Icon string 26 | CreateType CreateType 27 | UniqueHash string 28 | ExternalTemplateId string 29 | WechatOAControls workwx.OATemplateControls 30 | Rules []map[string]interface{} 31 | Options map[string]interface{} 32 | Desc string 33 | } 34 | 35 | type TemplateCombination struct { 36 | Id int64 `json:"id"` 37 | Name string `json:"name"` 38 | Icon string `json:"icon"` 39 | Total int `json:"total"` 40 | CreateType CreateType 41 | Templates []Template `json:"templates"` 42 | } 43 | -------------------------------------------------------------------------------- /internal/template/internal/domain/group.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type TemplateGroup struct { 4 | Id int64 5 | Name string 6 | Icon string 7 | } 8 | -------------------------------------------------------------------------------- /internal/template/internal/domain/wechat.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type WechatInfo struct { 4 | TemplateId string 5 | TemplateName string 6 | SpNo string 7 | } 8 | -------------------------------------------------------------------------------- /internal/template/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/template/internal/event/producer.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mqx" 6 | "github.com/ecodeclub/mq-api" 7 | "github.com/xen0n/go-workwx" 8 | ) 9 | 10 | type WechatOrderEventProducer interface { 11 | Produce(ctx context.Context, evt *workwx.OAApprovalDetail) error 12 | } 13 | 14 | func NewWechatOrderEventProducer(q mq.MQ) (WechatOrderEventProducer, error) { 15 | return mqx.NewGeneralProducer[*workwx.OAApprovalDetail](q, WechatOrderEventName) 16 | } 17 | -------------------------------------------------------------------------------- /internal/template/internal/event/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ecodeclub 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package event 16 | 17 | const ( 18 | // WechatOrderEventName 自动创建工单 19 | WechatOrderEventName = "wechat_order_events" 20 | 21 | // WechatCallbackEventName 接收来自企业微信审批通过消息 22 | WechatCallbackEventName = "wechat_callback_events" 23 | ) 24 | -------------------------------------------------------------------------------- /internal/template/internal/repository/error.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "github.com/Duke1616/ecmdb/internal/template/internal/repository/dao" 4 | 5 | var ErrUserNotFound = dao.ErrDataNotFound 6 | -------------------------------------------------------------------------------- /internal/template/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/template/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/template/module.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/template/internal/event" 5 | ) 6 | 7 | type Module struct { 8 | Svc Service 9 | c *event.WechatApprovalCallbackConsumer 10 | Hdl *Handler 11 | GroupHdl *GroupHdl 12 | } 13 | -------------------------------------------------------------------------------- /internal/template/types.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/template/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/template/internal/web" 6 | ) 7 | 8 | type Handler = web.Handler 9 | 10 | type GroupHdl = web.GroupHandler 11 | 12 | type Service = service.Service 13 | -------------------------------------------------------------------------------- /internal/template/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package template 4 | 5 | import ( 6 | "context" 7 | "github.com/Duke1616/ecmdb/internal/template/internal/event" 8 | "github.com/Duke1616/ecmdb/internal/template/internal/repository" 9 | "github.com/Duke1616/ecmdb/internal/template/internal/repository/dao" 10 | "github.com/Duke1616/ecmdb/internal/template/internal/service" 11 | "github.com/Duke1616/ecmdb/internal/template/internal/web" 12 | "github.com/Duke1616/ecmdb/pkg/mongox" 13 | "github.com/ecodeclub/mq-api" 14 | "github.com/google/wire" 15 | "github.com/xen0n/go-workwx" 16 | ) 17 | 18 | var ProviderSet = wire.NewSet( 19 | web.NewHandler, 20 | service.NewService, 21 | repository.NewTemplateRepository, 22 | dao.NewTemplateDAO, 23 | web.NewGroupHandler, 24 | service.NewGroupService, 25 | repository.NewTemplateGroupRepository, 26 | dao.NewTemplateGroupDAO, 27 | ) 28 | 29 | func InitModule(q mq.MQ, db *mongox.Mongo, workAPP *workwx.WorkwxApp) (*Module, error) { 30 | wire.Build( 31 | ProviderSet, 32 | event.NewWechatOrderEventProducer, 33 | initConsumer, 34 | wire.Struct(new(Module), "*"), 35 | ) 36 | return new(Module), nil 37 | } 38 | 39 | func initConsumer(svc service.Service, q mq.MQ, p event.WechatOrderEventProducer, workAPP *workwx.WorkwxApp) *event.WechatApprovalCallbackConsumer { 40 | consumer, err := event.NewWechatApprovalCallbackConsumer(svc, q, p, workAPP) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | consumer.Start(context.Background()) 46 | return consumer 47 | } 48 | -------------------------------------------------------------------------------- /internal/terminal/internal/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | -------------------------------------------------------------------------------- /internal/terminal/internal/web/session.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/pkg/term/guacx" 5 | "github.com/gorilla/websocket" 6 | "sync" 7 | ) 8 | 9 | type Session struct { 10 | Websocket *websocket.Conn 11 | Tunnel *guacx.Tunnel 12 | mutex sync.Mutex 13 | } 14 | 15 | func (s *Session) Close() { 16 | if s.Tunnel != nil { 17 | _ = s.Tunnel.Close() 18 | } 19 | 20 | if s.Websocket != nil { 21 | _ = s.Websocket.Close() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/terminal/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type CreateTunnelReq struct { 4 | Width string `json:"width"` 5 | Height string `json:"height"` 6 | Dpi string `json:"dpi"` 7 | } 8 | 9 | type ConnectReq struct { 10 | Type string `json:"type"` 11 | ResourceId int64 `json:"resource_id"` 12 | } 13 | -------------------------------------------------------------------------------- /internal/terminal/module.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | -------------------------------------------------------------------------------- /internal/terminal/types.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import "github.com/Duke1616/ecmdb/internal/terminal/internal/web" 4 | 5 | type Handler = web.Handler 6 | -------------------------------------------------------------------------------- /internal/terminal/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package terminal 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/attribute" 7 | "github.com/Duke1616/ecmdb/internal/relation" 8 | "github.com/Duke1616/ecmdb/internal/resource" 9 | "github.com/Duke1616/ecmdb/internal/terminal/internal/web" 10 | "github.com/google/wire" 11 | ) 12 | 13 | func InitModule(relationModule *relation.Module, resourceModule *resource.Module, attributeModule *attribute.Module) (*web.Handler, error) { 14 | wire.Build( 15 | web.NewHandler, 16 | wire.FieldsOf(new(*relation.Module), "RRSvc"), 17 | wire.FieldsOf(new(*resource.Module), "Svc"), 18 | wire.FieldsOf(new(*attribute.Module), "Svc"), 19 | ) 20 | return new(web.Handler), nil 21 | } 22 | -------------------------------------------------------------------------------- /internal/terminal/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package terminal 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/attribute" 11 | "github.com/Duke1616/ecmdb/internal/relation" 12 | "github.com/Duke1616/ecmdb/internal/resource" 13 | "github.com/Duke1616/ecmdb/internal/terminal/internal/web" 14 | ) 15 | 16 | // Injectors from wire.go: 17 | 18 | func InitModule(relationModule *relation.Module, resourceModule *resource.Module, attributeModule *attribute.Module) (*web.Handler, error) { 19 | relationResourceService := relationModule.RRSvc 20 | service := resourceModule.Svc 21 | serviceService := attributeModule.Svc 22 | handler := web.NewHandler(relationResourceService, service, serviceService) 23 | return handler, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/test/ioc/db.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mongox" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | "log" 9 | "time" 10 | ) 11 | 12 | func InitMongoDB() *mongox.Mongo { 13 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 14 | defer cancel() 15 | 16 | opts := options.Client(). 17 | ApplyURI("mongodb://root:123456@127.0.0.1:27017/admin") 18 | client, err := mongo.Connect(ctx, opts) 19 | 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | if err = client.Ping(ctx, nil); err != nil { 25 | log.Panicf("ping mongodb server error, %s", err) 26 | } 27 | 28 | return mongox.NewMongo(client, "cmdb-e2e") 29 | } 30 | -------------------------------------------------------------------------------- /internal/tools/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ValidationError = ErrorCode{Code: 503002, Msg: "验证错误"} 6 | ) 7 | 8 | type ErrorCode struct { 9 | Code int 10 | Msg string 11 | } 12 | -------------------------------------------------------------------------------- /internal/tools/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/minio/minio-go/v7" 6 | "net/url" 7 | "time" 8 | ) 9 | 10 | type Service interface { 11 | GetPresignedUrl(ctx context.Context, bucketName string, objectName string) (*url.URL, error) 12 | PutPresignedUrl(ctx context.Context, bucketName string, objectName string) (*url.URL, error) 13 | RemoveObject(ctx context.Context, bucketName string, objetName string) error 14 | } 15 | 16 | type service struct { 17 | minioClient *minio.Client 18 | expires time.Duration 19 | } 20 | 21 | func NewService(minioClient *minio.Client) Service { 22 | return &service{ 23 | minioClient: minioClient, 24 | expires: time.Minute * 2, 25 | } 26 | } 27 | 28 | func (s *service) PutPresignedUrl(ctx context.Context, bucketName string, objectName string) (*url.URL, error) { 29 | return s.minioClient.PresignedPutObject(ctx, bucketName, objectName, s.expires) 30 | } 31 | 32 | func (s *service) GetPresignedUrl(ctx context.Context, bucketName string, objectName string) (*url.URL, error) { 33 | reqParams := make(url.Values) 34 | reqParams.Set("response-content-disposition", "attachment; filename="+objectName) 35 | return s.minioClient.PresignedGetObject(ctx, bucketName, objectName, s.expires, reqParams) 36 | } 37 | 38 | func (s *service) RemoveObject(ctx context.Context, bucketName string, objetName string) error { 39 | return s.minioClient.RemoveObject(ctx, bucketName, objetName, minio.RemoveObjectOptions{ 40 | ForceDelete: true, 41 | GovernanceBypass: true, 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /internal/tools/types.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "github.com/Duke1616/ecmdb/internal/tools/web" 4 | 5 | type Handler = web.Handler 6 | -------------------------------------------------------------------------------- /internal/tools/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/tools/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | 14 | validationErrorResult = ginx.Result{ 15 | Code: errs.ValidationError.Code, 16 | Msg: errs.ValidationError.Msg, 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /internal/tools/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type GetPresignedUrl struct { 4 | ObjectName string `json:"object_name"` 5 | } 6 | 7 | type PutPresignedUrl struct { 8 | ObjectName string `json:"object_name"` 9 | } 10 | 11 | type RemoveObjectReq struct { 12 | ObjectName string `json:"object_name"` 13 | } 14 | -------------------------------------------------------------------------------- /internal/tools/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package tools 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/tools/service" 7 | "github.com/Duke1616/ecmdb/internal/tools/web" 8 | "github.com/google/wire" 9 | "github.com/minio/minio-go/v7" 10 | ) 11 | 12 | func InitModule(minioClient *minio.Client) (*web.Handler, error) { 13 | wire.Build( 14 | web.NewHandler, 15 | service.NewService, 16 | ) 17 | return new(web.Handler), nil 18 | } 19 | -------------------------------------------------------------------------------- /internal/tools/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package tools 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/tools/service" 11 | "github.com/Duke1616/ecmdb/internal/tools/web" 12 | "github.com/minio/minio-go/v7" 13 | ) 14 | 15 | // Injectors from wire.go: 16 | 17 | func InitModule(minioClient *minio.Client) (*web.Handler, error) { 18 | serviceService := service.NewService(minioClient) 19 | handler := web.NewHandler(serviceService) 20 | return handler, nil 21 | } 22 | -------------------------------------------------------------------------------- /internal/user/REAEME.md: -------------------------------------------------------------------------------- 1 | # 用户模块 2 | ## 集成 LDAP 3 | 4 | ## 集成 Passkey -------------------------------------------------------------------------------- /internal/user/internal/domain/feishu.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type FeishuInfo struct { 4 | UserId string `json:"user_id"` 5 | } 6 | -------------------------------------------------------------------------------- /internal/user/internal/domain/ldap.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Profile struct { 4 | DN string `json:"dn"` 5 | Email string `json:"email"` 6 | Username string `json:"username"` 7 | Title string `json:"title"` 8 | WhenCreated string `json:"when_created"` 9 | DisplayName string `json:"display_name"` 10 | Groups []string `json:"groups"` 11 | } 12 | -------------------------------------------------------------------------------- /internal/user/internal/domain/user.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type CreateType uint8 4 | 5 | func (s CreateType) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // LDAP LDAP创建 11 | LDAP CreateType = 1 12 | // SYSTEM 系统创建 13 | SYSTEM CreateType = 2 14 | ) 15 | 16 | type Status uint8 17 | 18 | func (s Status) ToUint8() uint8 { 19 | return uint8(s) 20 | } 21 | 22 | const ( 23 | // ENABLED 启用 24 | ENABLED Status = 1 25 | // DISABLED 禁用 26 | DISABLED Status = 2 27 | ) 28 | 29 | type User struct { 30 | Id int64 `json:"id"` 31 | DepartmentId int64 `json:"department_id"` 32 | Username string `json:"username"` 33 | Password string `json:"password"` 34 | Email string `json:"email"` 35 | Title string `json:"title"` 36 | DisplayName string `json:"display_name"` 37 | Status Status `json:"status"` 38 | CreateType CreateType `json:"create_type"` 39 | RoleCodes []string `json:"role_codes"` 40 | FeishuInfo FeishuInfo `json:"feishu_info"` 41 | WechatInfo WechatInfo `json:"wechat_info"` 42 | } 43 | 44 | type UserCombination struct { 45 | DepartMentId int64 46 | Total int 47 | Users []User 48 | } 49 | -------------------------------------------------------------------------------- /internal/user/internal/domain/wechat.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type WechatInfo struct { 4 | UserId string `json:"user_id"` 5 | } 6 | -------------------------------------------------------------------------------- /internal/user/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ecodeclub 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errs 16 | 17 | var ( 18 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 19 | UserPassError = ErrorCode{Code: 504002, Msg: "账号或密码输入不正确"} 20 | ) 21 | 22 | type ErrorCode struct { 23 | Code int 24 | Msg string 25 | } 26 | -------------------------------------------------------------------------------- /internal/user/internal/job/sync.go: -------------------------------------------------------------------------------- 1 | package job 2 | -------------------------------------------------------------------------------- /internal/user/internal/service/passkey.go: -------------------------------------------------------------------------------- 1 | package service 2 | -------------------------------------------------------------------------------- /internal/user/internal/web/ldap.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type LdapUser struct { 4 | Username string `json:"username"` 5 | Email string `json:"email"` 6 | Title string `json:"title"` 7 | DisplayName string `json:"display_name"` 8 | IsSystemExist bool `json:"is_system_exist"` 9 | } 10 | 11 | type RetrieveLdapUsers struct { 12 | Users []LdapUser `json:"users"` 13 | Total int `json:"total"` 14 | } 15 | -------------------------------------------------------------------------------- /internal/user/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/user/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | userOrPassErrorResult = ginx.Result{ 14 | Code: errs.UserPassError.Code, 15 | Msg: errs.UserPassError.Msg, 16 | } 17 | ) 18 | -------------------------------------------------------------------------------- /internal/user/ldapx/conn.go: -------------------------------------------------------------------------------- 1 | package ldapx 2 | 3 | import "github.com/go-ldap/ldap/v3" 4 | 5 | type Connection interface { 6 | Bind(username, password string) error 7 | Close() 8 | Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) 9 | SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) 10 | } 11 | 12 | type ConnectionImpl struct { 13 | conn *ldap.Conn 14 | } 15 | 16 | func NewLDAPConnectionImpl(conn *ldap.Conn) *ConnectionImpl { 17 | return &ConnectionImpl{conn} 18 | } 19 | 20 | func (lc *ConnectionImpl) Bind(username, password string) error { 21 | return lc.conn.Bind(username, password) 22 | } 23 | 24 | func (lc *ConnectionImpl) Close() { 25 | lc.conn.Close() 26 | } 27 | 28 | func (lc *ConnectionImpl) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) { 29 | return lc.conn.Search(searchRequest) 30 | } 31 | 32 | func (lc *ConnectionImpl) SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) { 33 | return lc.conn.SearchWithPaging(searchRequest, pagingSize) 34 | } 35 | -------------------------------------------------------------------------------- /internal/user/ldapx/type.go: -------------------------------------------------------------------------------- 1 | package ldapx 2 | 3 | type Config struct { 4 | Url string `mapstructure:"url" json:"url,omitempty"` 5 | BaseDN string `mapstructure:"base_dn" json:"base_dn,omitempty"` 6 | BindDN string `mapstructure:"bind_dn" json:"bind_dn,omitempty"` 7 | BindPassword string `mapstructure:"bind_password" json:"bind_password,omitempty"` 8 | UsernameAttribute string `mapstructure:"username_attribute" json:"username_attribute,omitempty"` 9 | MailAttribute string `mapstructure:"mail_attribute" json:"mail_attribute,omitempty"` 10 | DisplayNameAttribute string `mapstructure:"display_name_attribute" json:"display_name_attribute,omitempty"` 11 | TitleAttribute string `mapstructure:"title_attribute" json:"title_attribute,omitempty"` 12 | WhenCreatedAttribute string `mapstructure:"when_created_attribute" json:"when_created_attribute,omitempty"` 13 | UserFilter string `mapstructure:"user_filter" json:"user_filter,omitempty"` 14 | SyncUserFilter string `mapstructure:"sync_user_filter" json:"sync_user_filter,omitempty"` 15 | SyncExcludeOu string `mapstructure:"sync_exclude_ou" json:"sync_exclude_ou,omitempty"` 16 | GroupFilter string `mapstructure:"group_filter" json:"group_filter"` 17 | GroupNameAttribute string `mapstructure:"group_name_attribute" json:"group_name_attribute"` 18 | } 19 | -------------------------------------------------------------------------------- /internal/user/module.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "github.com/Duke1616/ecmdb/internal/user/internal/web" 4 | 5 | type Module struct { 6 | Hdl *web.Handler 7 | Svc Service 8 | } 9 | -------------------------------------------------------------------------------- /internal/user/passkey/passkey.go: -------------------------------------------------------------------------------- 1 | package passkey 2 | -------------------------------------------------------------------------------- /internal/user/type.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/user/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/user/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/user/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type User = domain.User 14 | -------------------------------------------------------------------------------- /internal/user/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package user 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/department" 7 | "github.com/Duke1616/ecmdb/internal/policy" 8 | "github.com/Duke1616/ecmdb/internal/user/internal/repository" 9 | "github.com/Duke1616/ecmdb/internal/user/internal/repository/cache" 10 | "github.com/Duke1616/ecmdb/internal/user/internal/repository/dao" 11 | "github.com/Duke1616/ecmdb/internal/user/internal/service" 12 | "github.com/Duke1616/ecmdb/internal/user/internal/web" 13 | "github.com/Duke1616/ecmdb/internal/user/ldapx" 14 | "github.com/Duke1616/ecmdb/pkg/mongox" 15 | "github.com/RediSearch/redisearch-go/v2/redisearch" 16 | "github.com/google/wire" 17 | ) 18 | 19 | var ProviderSet = wire.NewSet( 20 | service.NewLdapService, 21 | service.NewService, 22 | repository.NewResourceRepository, 23 | dao.NewUserDao, 24 | web.NewHandler, 25 | ) 26 | 27 | func InitLdapUserCache(conn *redisearch.Client) cache.RedisearchLdapUserCache { 28 | return cache.NewRedisearchLdapUserCache(conn) 29 | } 30 | 31 | func InitModule(db *mongox.Mongo, redisClient *redisearch.Client, ldapConfig ldapx.Config, policyModule *policy.Module, 32 | departmentModule *department.Module) (*Module, error) { 33 | wire.Build( 34 | ProviderSet, 35 | InitLdapUserCache, 36 | wire.Struct(new(Module), "*"), 37 | wire.FieldsOf(new(*department.Module), "Svc"), 38 | wire.FieldsOf(new(*policy.Module), "Svc"), 39 | ) 40 | return new(Module), nil 41 | } 42 | -------------------------------------------------------------------------------- /internal/worker/internal/domain/worker.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type Status uint8 4 | 5 | func (s Status) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // RUNNING 启用 11 | RUNNING Status = 1 12 | // STOPPING 停止 13 | STOPPING Status = 2 14 | // OFFLINE 离线 15 | OFFLINE Status = 3 16 | ) 17 | 18 | type Worker struct { 19 | Id int64 20 | Key string 21 | Name string 22 | Desc string 23 | Topic string 24 | Status Status 25 | } 26 | 27 | type Execute struct { 28 | Topic string 29 | TaskId int64 30 | Language string 31 | Code string 32 | Args map[string]interface{} 33 | Variables string 34 | } 35 | -------------------------------------------------------------------------------- /internal/worker/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/worker/internal/event/producer.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/pkg/mqx" 6 | "github.com/ecodeclub/mq-api" 7 | ) 8 | 9 | type TaskWorkerEventProducer interface { 10 | Produce(ctx context.Context, topic string, evt EworkRunnerExecuteEvent) error 11 | AddProducer(topic string) error 12 | DelProducer(topic string) error 13 | } 14 | 15 | func NewTaskRunnerEventProducer(q mq.MQ) (TaskWorkerEventProducer, error) { 16 | return mqx.NewMultipleProducer[EworkRunnerExecuteEvent](q) 17 | } 18 | -------------------------------------------------------------------------------- /internal/worker/internal/event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | const TaskWorkerEventName = "task_worker_events" 4 | 5 | type Status uint8 6 | 7 | func (s Status) ToUint8() uint8 { 8 | return uint8(s) 9 | } 10 | 11 | const ( 12 | // RUNNING 启用 13 | RUNNING Status = 1 14 | // STOPPING 停止 15 | STOPPING Status = 2 16 | ) 17 | 18 | type WorkerEvent struct { 19 | Name string `json:"name"` 20 | Desc string `json:"desc"` 21 | Topic string `json:"topic"` 22 | Status Status `json:"status"` 23 | } 24 | 25 | type EworkRunnerExecuteEvent struct { 26 | TaskId int64 `json:"task_id"` 27 | Language string `json:"language"` 28 | Code string `json:"code"` 29 | Args map[string]interface{} `json:"args"` 30 | Variables string `json:"variables"` 31 | } 32 | -------------------------------------------------------------------------------- /internal/worker/internal/repository/error.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "github.com/Duke1616/ecmdb/internal/worker/internal/repository/dao" 4 | 5 | var ErrUserNotFound = dao.ErrDataNotFound 6 | -------------------------------------------------------------------------------- /internal/worker/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/worker/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/worker/internal/web/vo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type Status uint8 4 | 5 | func (s Status) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // RUNNING 启用 11 | RUNNING Status = 1 12 | // STOPPING 停止 13 | STOPPING Status = 2 14 | ) 15 | 16 | type Page struct { 17 | Offset int64 `json:"offset,omitempty"` 18 | Limit int64 `json:"limit,omitempty"` 19 | } 20 | 21 | type ListWorkerReq struct { 22 | Page 23 | } 24 | 25 | type Worker struct { 26 | Id int64 `json:"id"` 27 | Name string `json:"name"` 28 | Desc string `json:"desc"` 29 | Topic string `json:"topic"` 30 | Status Status `json:"status"` 31 | } 32 | 33 | type RetrieveWorkers struct { 34 | Total int64 `json:"total"` 35 | Workers []Worker `json:"workers"` 36 | } 37 | 38 | type PushMessageReq struct { 39 | Name string `json:"name"` 40 | UUID string `json:"uuid"` 41 | Language string `json:"language"` 42 | Code string `json:"code"` 43 | Topic string `json:"topic"` 44 | } 45 | -------------------------------------------------------------------------------- /internal/worker/module.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/worker/internal/event/watch" 5 | ) 6 | 7 | type Module struct { 8 | Svc Service 9 | w *watch.TaskWorkerWatch 10 | Hdl *Handler 11 | } 12 | -------------------------------------------------------------------------------- /internal/worker/types.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/worker/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/worker/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/worker/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Execute = domain.Execute 14 | 15 | type Worker = domain.Worker 16 | 17 | const ( 18 | RUNNING = domain.RUNNING 19 | STOPPING = domain.STOPPING 20 | OFFLINE = domain.OFFLINE 21 | ) 22 | -------------------------------------------------------------------------------- /internal/workflow/internal/domain/logicflow.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | // Edge 定义线字段 4 | type Edge struct { 5 | Type string `json:"type"` 6 | SourceNodeId string `json:"sourceNodeId"` 7 | TargetNodeId string `json:"targetNodeId"` 8 | Properties interface{} `json:"properties"` 9 | ID string `json:"id"` 10 | } 11 | 12 | // Node 节点定义 13 | type Node struct { 14 | Type string `json:"type"` 15 | Properties interface{} `json:"properties"` 16 | ID string `json:"id"` 17 | } 18 | 19 | type EdgeProperty struct { 20 | Expression string `json:"expression"` 21 | } 22 | 23 | type UserProperty struct { 24 | Name string `json:"name"` 25 | Approved string `json:"approved"` 26 | } 27 | 28 | type StartProperty struct { 29 | Name string `json:"name"` 30 | } 31 | 32 | type EndProperty struct { 33 | Name string `json:"name"` 34 | } 35 | 36 | type ConditionProperty struct { 37 | Name string `json:"name"` 38 | } 39 | -------------------------------------------------------------------------------- /internal/workflow/internal/domain/workflow.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | type NotifyMethod uint8 4 | 5 | func (s NotifyMethod) ToUint8() uint8 { 6 | return uint8(s) 7 | } 8 | 9 | const ( 10 | // Feishu 飞书 11 | Feishu NotifyMethod = 1 12 | // Wechat 企业微信 13 | Wechat NotifyMethod = 2 14 | ) 15 | 16 | type Workflow struct { 17 | Id int64 18 | TemplateId int64 19 | Name string 20 | Icon string 21 | Owner string 22 | Desc string 23 | IsNotify bool 24 | NotifyMethod NotifyMethod 25 | FlowData LogicFlow // 前端数据传递Flow数据 26 | ProcessId int // 绑定对应的后端引擎 ID 27 | } 28 | 29 | type LogicFlow struct { 30 | Edges []map[string]interface{} `json:"edges"` 31 | Nodes []map[string]interface{} `json:"nodes"` 32 | } 33 | -------------------------------------------------------------------------------- /internal/workflow/internal/errs/code.go: -------------------------------------------------------------------------------- 1 | package errs 2 | 3 | var ( 4 | SystemError = ErrorCode{Code: 503001, Msg: "系统错误"} 5 | ) 6 | 7 | type ErrorCode struct { 8 | Code int 9 | Msg string 10 | } 11 | -------------------------------------------------------------------------------- /internal/workflow/internal/web/result.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/workflow/internal/errs" 5 | "github.com/Duke1616/ecmdb/pkg/ginx" 6 | ) 7 | 8 | var ( 9 | systemErrorResult = ginx.Result{ 10 | Code: errs.SystemError.Code, 11 | Msg: errs.SystemError.Msg, 12 | } 13 | ) 14 | -------------------------------------------------------------------------------- /internal/workflow/module.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/workflow/internal/service" 5 | "github.com/Duke1616/ecmdb/internal/workflow/internal/web" 6 | ) 7 | 8 | type Module struct { 9 | Hdl *web.Handler 10 | Svc service.Service 11 | } 12 | -------------------------------------------------------------------------------- /internal/workflow/types.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/workflow/internal/domain" 5 | "github.com/Duke1616/ecmdb/internal/workflow/internal/service" 6 | "github.com/Duke1616/ecmdb/internal/workflow/internal/web" 7 | ) 8 | 9 | type Handler = web.Handler 10 | 11 | type Service = service.Service 12 | 13 | type Workflow = domain.Workflow 14 | 15 | type NotifyMethod = domain.NotifyMethod 16 | 17 | // NotifyMethodToString 将 NotifyMethod 转换为对应的文字描述 18 | func NotifyMethodToString(method NotifyMethod) string { 19 | switch method { 20 | case domain.Feishu: 21 | return "feishu" 22 | case domain.Wechat: 23 | return "wechat" 24 | default: 25 | return "Unknown" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/workflow/wire.go: -------------------------------------------------------------------------------- 1 | //go:build wireinject 2 | 3 | package workflow 4 | 5 | import ( 6 | "github.com/Duke1616/ecmdb/internal/engine" 7 | "github.com/Duke1616/ecmdb/internal/workflow/internal/repository" 8 | "github.com/Duke1616/ecmdb/internal/workflow/internal/repository/dao" 9 | "github.com/Duke1616/ecmdb/internal/workflow/internal/service" 10 | "github.com/Duke1616/ecmdb/internal/workflow/internal/web" 11 | "github.com/Duke1616/ecmdb/internal/workflow/pkg/easyflow" 12 | "github.com/Duke1616/ecmdb/pkg/mongox" 13 | "github.com/google/wire" 14 | ) 15 | 16 | var ProviderSet = wire.NewSet( 17 | web.NewHandler, 18 | service.NewService, 19 | repository.NewWorkflowRepository, 20 | dao.NewWorkflowDAO, 21 | easyflow.NewLogicFlowToEngineConvert, 22 | ) 23 | 24 | func InitModule(db *mongox.Mongo, engineModule *engine.Module) (*Module, error) { 25 | wire.Build( 26 | ProviderSet, 27 | wire.FieldsOf(new(*engine.Module), "Svc"), 28 | wire.Struct(new(Module), "*"), 29 | ) 30 | return new(Module), nil 31 | } 32 | -------------------------------------------------------------------------------- /internal/workflow/wire_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by Wire. DO NOT EDIT. 2 | 3 | //go:generate go run github.com/google/wire/cmd/wire 4 | //go:build !wireinject 5 | // +build !wireinject 6 | 7 | package workflow 8 | 9 | import ( 10 | "github.com/Duke1616/ecmdb/internal/engine" 11 | "github.com/Duke1616/ecmdb/internal/workflow/internal/repository" 12 | "github.com/Duke1616/ecmdb/internal/workflow/internal/repository/dao" 13 | "github.com/Duke1616/ecmdb/internal/workflow/internal/service" 14 | "github.com/Duke1616/ecmdb/internal/workflow/internal/web" 15 | "github.com/Duke1616/ecmdb/internal/workflow/pkg/easyflow" 16 | "github.com/Duke1616/ecmdb/pkg/mongox" 17 | "github.com/google/wire" 18 | ) 19 | 20 | // Injectors from wire.go: 21 | 22 | func InitModule(db *mongox.Mongo, engineModule *engine.Module) (*Module, error) { 23 | workflowDAO := dao.NewWorkflowDAO(db) 24 | workflowRepository := repository.NewWorkflowRepository(workflowDAO) 25 | processEngineConvert := easyflow.NewLogicFlowToEngineConvert() 26 | serviceService := service.NewService(workflowRepository, processEngineConvert) 27 | service2 := engineModule.Svc 28 | handler := web.NewHandler(serviceService, service2) 29 | module := &Module{ 30 | Hdl: handler, 31 | Svc: serviceService, 32 | } 33 | return module, nil 34 | } 35 | 36 | // wire.go: 37 | 38 | var ProviderSet = wire.NewSet(web.NewHandler, service.NewService, repository.NewWorkflowRepository, dao.NewWorkflowDAO, easyflow.NewLogicFlowToEngineConvert) 39 | -------------------------------------------------------------------------------- /ioc/app.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "github.com/Duke1616/ecmdb/internal/endpoint" 5 | "github.com/Duke1616/ecmdb/internal/event/service/easyflow" 6 | "github.com/gin-gonic/gin" 7 | "github.com/gotomicro/ego/task/ecron" 8 | ) 9 | 10 | type App struct { 11 | Web *gin.Engine 12 | Event *easyflow.ProcessEvent 13 | Jobs []*ecron.Component 14 | Svc endpoint.Service 15 | } 16 | -------------------------------------------------------------------------------- /ioc/db.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Duke1616/ecmdb/pkg/mongox" 7 | "github.com/spf13/viper" 8 | "go.mongodb.org/mongo-driver/event" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | "log" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func InitMongoDB() *mongox.Mongo { 17 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 18 | defer cancel() 19 | 20 | monitor := &event.CommandMonitor{ 21 | Started: func(ctx context.Context, evt *event.CommandStartedEvent) { 22 | //fmt.Println(evt.Command) 23 | }, 24 | } 25 | 26 | type Config struct { 27 | DSN string `mapstructure:"dsn"` 28 | DB string `mapstructure:"db"` 29 | Username string `mapstructure:"username"` 30 | Password string `mapstructure:"password"` 31 | } 32 | 33 | var cfg Config 34 | if err := viper.UnmarshalKey("mongodb", &cfg); err != nil { 35 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 36 | } 37 | dsn := strings.Split(cfg.DSN, "//") 38 | uri := fmt.Sprintf("%s//%s:%s@%s", dsn[0], cfg.Username, cfg.Password, dsn[1]) 39 | 40 | opts := options.Client(). 41 | ApplyURI(uri). 42 | SetMonitor(monitor) 43 | client, err := mongo.Connect(ctx, opts) 44 | 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | if err = client.Ping(ctx, nil); err != nil { 50 | log.Panicf("ping mongodb server error, %s", err) 51 | } 52 | 53 | return mongox.NewMongo(client, cfg.DB) 54 | } 55 | -------------------------------------------------------------------------------- /ioc/etcd.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/spf13/viper" 7 | clientv3 "go.etcd.io/etcd/client/v3" 8 | "time" 9 | ) 10 | 11 | func InitEtcdClient() *clientv3.Client { 12 | var cfg clientv3.Config 13 | 14 | // Unmarshal etcd configuration from viper 15 | if err := viper.UnmarshalKey("etcd", &cfg); err != nil { 16 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 17 | } 18 | 19 | // Set connection timeout (e.g., 5 seconds) 20 | cfg.DialTimeout = 5 * time.Second 21 | 22 | // Create the etcd client 23 | client, err := clientv3.New(cfg) 24 | if err != nil { 25 | panic(fmt.Errorf("failed to connect to etcd: %v", err)) 26 | } 27 | 28 | // Perform a ping test to ensure the etcd server is reachable 29 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 30 | defer cancel() 31 | 32 | // Get the status of the first available endpoint 33 | _, err = client.Status(ctx, client.Endpoints()[0]) 34 | if err != nil { 35 | panic(fmt.Errorf("failed to ping etcd: %v", err)) 36 | } 37 | 38 | return client 39 | 40 | return client 41 | } 42 | -------------------------------------------------------------------------------- /ioc/feishu.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | lark "github.com/larksuite/oapi-sdk-go/v3" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func InitFeishu() *lark.Client { 10 | type Config struct { 11 | AppId string `mapstructure:"appId"` 12 | AppSecret string `mapstructure:"appSecret"` 13 | } 14 | 15 | var cfg Config 16 | if err := viper.UnmarshalKey("feishu", &cfg); err != nil { 17 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 18 | } 19 | 20 | return lark.NewClient(cfg.AppId, cfg.AppSecret) 21 | } 22 | -------------------------------------------------------------------------------- /ioc/job.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "context" 5 | "github.com/Duke1616/ecmdb/internal/task" 6 | "github.com/gotomicro/ego/core/elog" 7 | "github.com/gotomicro/ego/task/ecron" 8 | "time" 9 | ) 10 | 11 | func initCronJobs(tJob *task.StartTaskJob, pJob *task.PassProcessTaskJob) []*ecron.Component { 12 | loc, _ := time.LoadLocation("Asia/Shanghai") 13 | 14 | return []*ecron.Component{ 15 | ecron.DefaultContainer().Build( 16 | ecron.WithJob(funcJobWrapper(tJob)), 17 | ecron.WithSeconds(), 18 | ecron.WithSpec("*/10 * * * * *"), 19 | ecron.WithLocation(loc), 20 | ), 21 | ecron.DefaultContainer().Build( 22 | ecron.WithJob(funcJobWrapper(pJob)), 23 | ecron.WithSeconds(), 24 | ecron.WithSpec("*/10 * * * * *"), 25 | ecron.WithLocation(loc), 26 | ), 27 | } 28 | } 29 | 30 | func funcJobWrapper(job ecron.NamedJob) ecron.FuncJob { 31 | name := job.Name() 32 | return func(ctx context.Context) error { 33 | start := time.Now() 34 | elog.DefaultLogger.Debug("开始运行", 35 | elog.String("cronjob", name)) 36 | err := job.Run(ctx) 37 | if err != nil { 38 | elog.DefaultLogger.Error("执行失败", 39 | elog.FieldErr(err), 40 | elog.String("cronjob", name)) 41 | return err 42 | } 43 | duration := time.Since(start) 44 | elog.DefaultLogger.Debug("结束运行", 45 | elog.String("cronjob", name), 46 | elog.FieldKey("运行时间"), 47 | elog.FieldCost(duration)) 48 | return nil 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ioc/kafka/addr.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | var addr = []string{"127.0.0.1:9094"} 4 | -------------------------------------------------------------------------------- /ioc/ldap.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Duke1616/ecmdb/internal/user/ldapx" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func InitLdapConfig() ldapx.Config { 10 | // 定义一个结构体实例 11 | var cfg ldapx.Config 12 | 13 | // 使用 Unmarshal 函数将配置数据解析到结构体中 14 | if err := viper.UnmarshalKey("ldap", &cfg); err != nil { 15 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 16 | } 17 | 18 | return cfg 19 | } 20 | -------------------------------------------------------------------------------- /ioc/minio.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/minio/minio-go/v7" 6 | "github.com/minio/minio-go/v7/pkg/credentials" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func InitMinioClient() *minio.Client { 11 | type Config struct { 12 | Endpoint string `mapstructure:"endpoint"` 13 | AccessKeyId string `mapstructure:"accessKeyID"` 14 | SecretAccessKey string `mapstructure:"secretAccessKey"` 15 | UseSSL bool `mapstructure:"useSSL"` 16 | } 17 | var cfg Config 18 | if err := viper.UnmarshalKey("minio", &cfg); err != nil { 19 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 20 | } 21 | 22 | // 初始化 Minio 客户端 23 | minioClient, err := minio.New(cfg.Endpoint, &minio.Options{ 24 | Creds: credentials.NewStaticV4(cfg.AccessKeyId, cfg.SecretAccessKey, ""), 25 | Secure: cfg.UseSSL, 26 | }) 27 | 28 | if err != nil { 29 | panic(err) 30 | } 31 | return minioClient 32 | } 33 | -------------------------------------------------------------------------------- /ioc/mq.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ecodeclub/ekit/retry" 6 | "github.com/ecodeclub/mq-api" 7 | "github.com/ecodeclub/mq-api/kafka" 8 | "github.com/spf13/viper" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | q mq.MQ 15 | mqInitOnce sync.Once 16 | ) 17 | 18 | func InitMQ() mq.MQ { 19 | mqInitOnce.Do(func() { 20 | const maxInterval = 10 * time.Second 21 | const maxRetries = 10 22 | strategy, err := retry.NewExponentialBackoffRetryStrategy(time.Second, maxInterval, maxRetries) 23 | if err != nil { 24 | panic(err) 25 | } 26 | for { 27 | q, err = initMQ() 28 | if err == nil { 29 | break 30 | } 31 | next, ok := strategy.Next() 32 | if !ok { 33 | panic("InitMQ 重试失败......") 34 | } 35 | time.Sleep(next) 36 | } 37 | }) 38 | return q 39 | } 40 | 41 | func initMQ() (mq.MQ, error) { 42 | type Config struct { 43 | Network string `yaml:"network"` 44 | Addresses []string `yaml:"addresses"` 45 | } 46 | 47 | var cfg Config 48 | if err := viper.UnmarshalKey("kafka", &cfg); err != nil { 49 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 50 | } 51 | 52 | qq, err := kafka.NewMQ(cfg.Network, cfg.Addresses) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return qq, nil 57 | } 58 | -------------------------------------------------------------------------------- /ioc/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ecodeclub 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ioc 16 | 17 | import ( 18 | "fmt" 19 | "github.com/redis/go-redis/v9" 20 | "github.com/spf13/viper" 21 | ) 22 | 23 | func InitRedis() redis.Cmdable { 24 | type Config struct { 25 | Addr string `mapstructure:"addr"` 26 | Password string `mapstructure:"password"` 27 | DB int `mapstructure:"db"` 28 | } 29 | 30 | var cfg Config 31 | 32 | if err := viper.UnmarshalKey("redis", &cfg); err != nil { 33 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 34 | } 35 | 36 | cmd := redis.NewClient(&redis.Options{ 37 | Addr: cfg.Addr, 38 | Password: cfg.Password, 39 | DB: cfg.DB, 40 | }) 41 | return cmd 42 | } 43 | -------------------------------------------------------------------------------- /ioc/redisearch.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RediSearch/redisearch-go/v2/redisearch" 6 | "github.com/gomodule/redigo/redis" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func InitRediSearch() *redisearch.Client { 11 | type Config struct { 12 | Addr string `mapstructure:"addr"` 13 | Password string `mapstructure:"password"` 14 | DB int `mapstructure:"db"` 15 | } 16 | 17 | var cfg Config 18 | if err := viper.UnmarshalKey("redis", &cfg); err != nil { 19 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 20 | } 21 | 22 | pool := &redis.Pool{Dial: func() (redis.Conn, error) { 23 | return redis.Dial("tcp", cfg.Addr, 24 | redis.DialPassword(cfg.Password), 25 | redis.DialDatabase(cfg.DB)) 26 | }} 27 | 28 | return redisearch.NewClientFromPool(pool, "index") 29 | } 30 | -------------------------------------------------------------------------------- /ioc/session.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ecodeclub/ginx/session" 6 | ginRedis "github.com/ecodeclub/ginx/session/redis" 7 | "github.com/redis/go-redis/v9" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | func InitSession(cmd redis.Cmdable) session.Provider { 12 | type Config struct { 13 | SessionEncryptedKey string `mapstructure:"session_encrypted_key"` 14 | } 15 | 16 | var cfg Config 17 | if err := viper.UnmarshalKey("session", &cfg); err != nil { 18 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 19 | } 20 | 21 | sp := ginRedis.NewSessionProvider(cmd, cfg.SessionEncryptedKey) 22 | 23 | return sp 24 | } 25 | -------------------------------------------------------------------------------- /ioc/workwx.go: -------------------------------------------------------------------------------- 1 | package ioc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/viper" 6 | "github.com/xen0n/go-workwx" 7 | ) 8 | 9 | func InitWorkWx() *workwx.WorkwxApp { 10 | type Config struct { 11 | // CorpSecret 应用的凭证密钥,必填 12 | CorpSecret string `yaml:"corpSecret"` 13 | // AgentID 应用 ID,必填 14 | AgentID int64 `json:"agentId"` 15 | // 企业微信 ID 16 | CorpID string `yaml:"corpId"` 17 | } 18 | 19 | var cfg Config 20 | if err := viper.UnmarshalKey("wechat", &cfg); err != nil { 21 | panic(fmt.Errorf("unable to decode into struct: %v", err)) 22 | } 23 | 24 | workApp := workwx.New(cfg.CorpID).WithApp(cfg.CorpSecret, cfg.AgentID) 25 | 26 | // refresh token 27 | go workApp.SpawnAccessTokenRefresher() 28 | 29 | return workApp 30 | } 31 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Duke1616/ecmdb/cmd" 6 | "github.com/fatih/color" 7 | git "github.com/purpleclay/gitz" 8 | ) 9 | 10 | var ( 11 | version string 12 | ) 13 | 14 | func main() { 15 | ver := version 16 | if version == "" { 17 | ver = latestTag() 18 | } 19 | 20 | fmt.Println(" ______ _____ __ __ _____ ____ ") 21 | fmt.Println(" | ____| / ____| | \\/ | | __ \\ | _ \\ ") 22 | fmt.Println(" | |__ | | | \\ / | | | | | | |_) |") 23 | fmt.Println(" | __| | | | |\\/| | | | | | | _ < ") 24 | fmt.Println(" | |____ | |____ | | | | | |__| | | |_) |") 25 | fmt.Println(" |______| \\_____| |_| |_| |_____/ |____/ ") 26 | 27 | // 使用颜色来突出显示 28 | cyan := color.New(color.FgCyan).SprintFunc() 29 | green := color.New(color.FgGreen).SprintFunc() 30 | fmt.Printf(" %s: %s\n", cyan("Service Version"), green(ver)) 31 | 32 | cmd.Execute(ver) 33 | } 34 | 35 | func latestTag() string { 36 | gc, err := git.NewClient() 37 | if err != nil { 38 | return "" 39 | } 40 | 41 | tags, _ := gc.Tags( 42 | git.WithShellGlob("*.*.*"), 43 | git.WithSortBy(git.CreatorDateDesc, git.VersionDesc), 44 | git.WithCount(1), 45 | ) 46 | 47 | return tags[0] 48 | } 49 | -------------------------------------------------------------------------------- /pkg/cryptox/aes_test.go: -------------------------------------------------------------------------------- 1 | package cryptox 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestDecryptAES(t *testing.T) { 9 | key := "1234567890" // Key must be 16, 24, or 32 bytes long 10 | data := map[string]interface{}{ 11 | "username": "user1", 12 | "password": "pass1", 13 | } 14 | 15 | // Encrypt 16 | encrypted, err := EncryptAES(key, data) 17 | if err != nil { 18 | fmt.Println("Encryption error:", err) 19 | return 20 | } 21 | fmt.Println("Encrypted:", encrypted) 22 | 23 | // Decrypt 24 | decryptedData, err := DecryptAES[any](key, encrypted) 25 | if err != nil { 26 | fmt.Println("Decryption error:", err) 27 | return 28 | } 29 | fmt.Println("Decrypted:", decryptedData) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/ginx/test/recorder.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/ecodeclub/ekit/net/httpx/httptestx" 6 | "net/http/httptest" 7 | ) 8 | 9 | func NewJSONResponseRecorder[T any]() *httptestx.JSONResponseRecorder[Result[T]] { 10 | return httptestx.NewJSONResponseRecorder[Result[T]]() 11 | } 12 | 13 | type JSONResponseRecorder[T any] struct { 14 | *httptest.ResponseRecorder 15 | } 16 | 17 | func (r JSONResponseRecorder[T]) Scan() (T, error) { 18 | var t T 19 | err := json.NewDecoder(r.Body).Decode(&t) 20 | return t, err 21 | } 22 | 23 | func (r JSONResponseRecorder[T]) MustScan() T { 24 | t, err := r.Scan() 25 | if err != nil { 26 | panic(err) 27 | } 28 | return t 29 | } 30 | -------------------------------------------------------------------------------- /pkg/ginx/test/result.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | type Result[T any] struct { 4 | Code int `json:"code"` 5 | Msg string `json:"msg"` 6 | Data T `json:"data"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/ginx/type.go: -------------------------------------------------------------------------------- 1 | package ginx 2 | 3 | type Result struct { 4 | Code int `json:"code"` 5 | Msg string `json:"msg"` 6 | Data any `json:"data"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/hash/hash.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/json" 6 | "fmt" 7 | "log/slog" 8 | ) 9 | 10 | func Hash(x interface{}) string { 11 | hash := sha1.New() 12 | b, err := json.Marshal(x) 13 | if err != nil { 14 | slog.Error("hash %v error, %s", x, err) 15 | return "" 16 | } 17 | hash.Write(b) 18 | return fmt.Sprintf("%x", hash.Sum(nil)) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/hash/hash_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestHash(t *testing.T) { 9 | test := Teacher{ 10 | Name: "张三", 11 | Age: 21, 12 | } 13 | fmt.Println(Hash(test)) 14 | } 15 | 16 | type Teacher struct { 17 | Name string 18 | Age int32 19 | } 20 | -------------------------------------------------------------------------------- /pkg/mongox/collection.go: -------------------------------------------------------------------------------- 1 | package mongox 2 | 3 | type Collection interface { 4 | Where() 5 | } 6 | 7 | type Coll struct { 8 | collName string // 集合名 9 | *Mongo 10 | } 11 | 12 | func (m *Coll) Where() { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /pkg/mongox/filter.go: -------------------------------------------------------------------------------- 1 | package mongox 2 | 3 | -------------------------------------------------------------------------------- /pkg/mongox/mongo.go: -------------------------------------------------------------------------------- 1 | package mongox 2 | 3 | import ( 4 | "context" 5 | "go.mongodb.org/mongo-driver/bson" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | ) 9 | 10 | type Mongo struct { 11 | DBClient *mongo.Client 12 | Sess mongo.Session 13 | dbName string 14 | } 15 | 16 | func NewMongo(client *mongo.Client, dbName string) *Mongo { 17 | return &Mongo{ 18 | DBClient: client, 19 | dbName: dbName, 20 | } 21 | } 22 | 23 | func (m *Mongo) Database() *mongo.Database { 24 | return m.DBClient.Database(m.dbName) 25 | } 26 | 27 | func (m *Mongo) Collection(collName string) *mongo.Collection { 28 | return m.Database().Collection(collName) 29 | } 30 | 31 | func (m *Mongo) Collections(collName string) Collection { 32 | col := Coll{} 33 | col.collName = collName 34 | return &col 35 | } 36 | 37 | func (m *Mongo) GetIdGenerator(collection string) int64 { 38 | coll := m.Database().Collection("c_id_generator") 39 | var result struct { 40 | Name string `json:"name" bson:"name"` 41 | NextID int64 `json:"next_id" bson:"next_id"` 42 | } 43 | 44 | update := bson.M{ 45 | "$inc": bson.M{"next_id": int64(1)}, 46 | } 47 | filter := bson.M{"name": collection} 48 | 49 | upsert := true 50 | returnChange := options.After 51 | opt := &options.FindOneAndUpdateOptions{ 52 | Upsert: &upsert, 53 | ReturnDocument: &returnChange, 54 | } 55 | 56 | err := coll.FindOneAndUpdate(context.Background(), filter, update, opt).Decode(&result) 57 | if err != nil { 58 | return 0 59 | } 60 | 61 | return result.NextID 62 | } 63 | -------------------------------------------------------------------------------- /pkg/mongox/sql.db: -------------------------------------------------------------------------------- 1 | db.c_id_generator.save({name:"c_model_group", next_id:NumberInt("0")}); -------------------------------------------------------------------------------- /pkg/mongox/tx.go: -------------------------------------------------------------------------------- 1 | package mongox 2 | -------------------------------------------------------------------------------- /pkg/mongox/type.go: -------------------------------------------------------------------------------- 1 | package mongox 2 | 3 | import "context" 4 | 5 | type MapStr map[string]interface{} 6 | 7 | type Filter interface{} 8 | 9 | type Querier[T any] interface { 10 | FindOne(ctx context.Context) (*T, error) 11 | FindMany(ctx context.Context) ([]*T, error) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/mqx/general_producer.go: -------------------------------------------------------------------------------- 1 | package mqx 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "github.com/ecodeclub/mq-api" 9 | ) 10 | 11 | type Producer[T any] interface { 12 | Produce(ctx context.Context, evt T) error 13 | } 14 | 15 | type GeneralProducer[T any] struct { 16 | producer mq.Producer 17 | topic string 18 | } 19 | 20 | func NewGeneralProducer[T any](q mq.MQ, topic string) (*GeneralProducer[T], error) { 21 | p, err := q.Producer(topic) 22 | return &GeneralProducer[T]{ 23 | producer: p, 24 | topic: topic, 25 | }, err 26 | } 27 | 28 | func (p *GeneralProducer[T]) Produce(ctx context.Context, evt T) error { 29 | data, err := json.Marshal(&evt) 30 | if err != nil { 31 | return fmt.Errorf("序列化失败: %w", err) 32 | } 33 | _, err = p.producer.Produce(ctx, &mq.Message{Value: data}) 34 | if err != nil { 35 | return fmt.Errorf("向topic=%s发送event=%#v失败: %w", p.topic, evt, err) 36 | } 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/registry/types.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | type Registry interface { 9 | Register(ctx context.Context, si Instance) error 10 | UnRegister(ctx context.Context, si Instance) error 11 | 12 | ListWorkers(ctx context.Context, name string) ([]Instance, error) 13 | Subscribe(name string) <-chan Event 14 | 15 | io.Closer 16 | } 17 | 18 | type Instance struct { 19 | Name string `yaml:"name" json:"name"` // 实例名称 20 | Desc string `yaml:"desc" json:"desc"` // 注解 21 | Topic string `yaml:"topic" json:"topic"` // 建立 Topic 通道 22 | } 23 | 24 | type EventType int 25 | 26 | const ( 27 | EventTypeUnknown EventType = iota 28 | EventTypeAdd 29 | EventTypeDelete 30 | ) 31 | 32 | type Event struct { 33 | Type EventType 34 | Key string 35 | Instance Instance 36 | } 37 | -------------------------------------------------------------------------------- /pkg/term/guacx/config.go: -------------------------------------------------------------------------------- 1 | package guacx 2 | 3 | type Config struct { 4 | ConnectionID string 5 | Protocol string 6 | Parameters map[string]string 7 | } 8 | 9 | func NewConfig() (config *Config) { 10 | config = &Config{} 11 | config.Parameters = make(map[string]string) 12 | return config 13 | } 14 | 15 | func (c *Config) SetParameter(name, value string) { 16 | c.Parameters[name] = value 17 | } 18 | 19 | func (c *Config) GetParameter(name string) string { 20 | return c.Parameters[name] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/term/guacx/handler.go: -------------------------------------------------------------------------------- 1 | package guacx 2 | 3 | import ( 4 | "context" 5 | "github.com/gorilla/websocket" 6 | ) 7 | 8 | type GuacamoleHandler struct { 9 | ws *websocket.Conn 10 | tunnel *Tunnel 11 | ctx context.Context 12 | cancel context.CancelFunc 13 | } 14 | 15 | func NewGuacamoleHandler(ws *websocket.Conn, tunnel *Tunnel) *GuacamoleHandler { 16 | ctx, cancel := context.WithCancel(context.Background()) 17 | return &GuacamoleHandler{ 18 | ws: ws, 19 | tunnel: tunnel, 20 | ctx: ctx, 21 | cancel: cancel, 22 | } 23 | } 24 | 25 | func (r GuacamoleHandler) Start() { 26 | go func() { 27 | for { 28 | select { 29 | case <-r.ctx.Done(): 30 | return 31 | default: 32 | instruction, err := r.tunnel.Read() 33 | if err != nil { 34 | return 35 | } 36 | if len(instruction) == 0 { 37 | continue 38 | } 39 | err = r.ws.WriteMessage(websocket.TextMessage, instruction) 40 | if err != nil { 41 | return 42 | } 43 | } 44 | } 45 | }() 46 | } 47 | 48 | func (r GuacamoleHandler) Stop() { 49 | r.cancel() 50 | _ = r.tunnel.Close() 51 | } 52 | -------------------------------------------------------------------------------- /pkg/term/guacx/instruction.go: -------------------------------------------------------------------------------- 1 | package guacx 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const delimiter = ';' 9 | 10 | type Instruction struct { 11 | Opcode string 12 | Args []string 13 | cache string 14 | } 15 | 16 | func NewInstruction(opcode string, args ...string) *Instruction { 17 | return &Instruction{ 18 | Opcode: opcode, 19 | Args: args, 20 | } 21 | } 22 | 23 | func (i *Instruction) String() string { 24 | if len(i.cache) > 0 { 25 | return i.cache 26 | } 27 | 28 | i.cache = fmt.Sprintf("%d.%s", len(i.Opcode), i.Opcode) 29 | for _, value := range i.Args { 30 | i.cache += fmt.Sprintf(",%d.%s", len(value), value) 31 | } 32 | 33 | i.cache += string(delimiter) 34 | return i.cache 35 | } 36 | 37 | func (i *Instruction) Bytes() []byte { 38 | return []byte(i.String()) 39 | } 40 | 41 | func (i *Instruction) Parse(content string) *Instruction { 42 | if strings.LastIndex(content, ";") > 0 { 43 | content = strings.TrimRight(content, ";") 44 | } 45 | elements := strings.Split(content, ",") 46 | 47 | var args = make([]string, len(elements)) 48 | for i, e := range elements { 49 | ss := strings.Split(e, ".") 50 | if len(ss) < 2 { 51 | continue 52 | } 53 | args[i] = ss[1] 54 | } 55 | return NewInstruction(args[0], args[1:]...) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/term/sessions.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/crypto/ssh" 6 | "sync" 7 | ) 8 | 9 | type Sessions struct { 10 | SshClient *ssh.Client 11 | } 12 | 13 | func NewSessions(sshClient *ssh.Client) *Sessions { 14 | return &Sessions{ 15 | SshClient: sshClient, 16 | } 17 | } 18 | 19 | type SessionPool struct { 20 | sessions map[int64]*Sessions 21 | mu *sync.Mutex 22 | } 23 | 24 | func NewSessionPool() *SessionPool { 25 | return &SessionPool{ 26 | sessions: make(map[int64]*Sessions), 27 | mu: &sync.Mutex{}, 28 | } 29 | } 30 | 31 | func (p *SessionPool) GetSession(id int64) (*Sessions, error) { 32 | p.mu.Lock() 33 | defer p.mu.Unlock() 34 | 35 | session, exists := p.sessions[id] 36 | if !exists { 37 | return nil, fmt.Errorf("session %d already exists", id) 38 | } 39 | 40 | return session, nil 41 | } 42 | 43 | func (p *SessionPool) SetSession(id int64, session *Sessions) { 44 | p.mu.Lock() 45 | defer p.mu.Unlock() 46 | 47 | p.sessions[id] = session 48 | } 49 | -------------------------------------------------------------------------------- /pkg/term/sshx/gatewhy.go: -------------------------------------------------------------------------------- 1 | package sshx 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | ) 7 | 8 | type GatewayConfig struct { 9 | AuthType string 10 | Host string 11 | Port int 12 | Username string 13 | Password string 14 | PrivateKey string 15 | Passphrase string 16 | Sort int 17 | } 18 | 19 | type MultiGatewayManager struct { 20 | Gateways []*GatewayConfig 21 | } 22 | 23 | func NewMultiGatewayManager(config []*GatewayConfig) *MultiGatewayManager { 24 | sort.Slice(config, func(i, j int) bool { 25 | return config[i].Sort < config[j].Sort 26 | }) 27 | 28 | return &MultiGatewayManager{ 29 | Gateways: config, 30 | } 31 | } 32 | 33 | // GetStringField 获取字段值,若不存在则返回默认值 34 | func GetStringField(data map[string]interface{}, key string, defaultValue string) string { 35 | if value, ok := data[key].(string); ok { 36 | return value 37 | } 38 | return defaultValue 39 | } 40 | 41 | // GetIntField 获取字段值,若不存在则返回默认值,返回值为 int 42 | func GetIntField(data map[string]interface{}, key string, defaultValue int) int { 43 | if value, ok := data[key].(string); ok { 44 | if intValue, err := strconv.Atoi(value); err == nil { 45 | return intValue 46 | } 47 | } 48 | return defaultValue 49 | } 50 | -------------------------------------------------------------------------------- /pkg/term/sshx/message.go: -------------------------------------------------------------------------------- 1 | package sshx 2 | 3 | import "encoding/json" 4 | 5 | type TerminalMessage struct { 6 | Operation string `json:"operation"` 7 | Data string `json:"data"` 8 | Cols int `json:"cols"` 9 | Rows int `json:"rows"` 10 | } 11 | 12 | func ParseTerminalMessage(value []byte) (TerminalMessage, error) { 13 | m := TerminalMessage{} 14 | err := json.Unmarshal(value, &m) 15 | return NewMessage(m.Operation, m.Data, m.Cols, m.Rows), err 16 | } 17 | 18 | func NewMessage(operation string, data string, cols, rows int) TerminalMessage { 19 | return TerminalMessage{Operation: operation, Data: data, Cols: cols, Rows: rows} 20 | } 21 | -------------------------------------------------------------------------------- /pkg/tools/slice.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | func ToMapBS[Ele any, Key comparable, Val any](elements []Ele, fn func(element Ele) (Key, Val)) map[Key]Val { 4 | resultMap := make(map[Key]Val) 5 | for _, element := range elements { 6 | k, v := fn(element) 7 | resultMap[k] = v 8 | } 9 | return resultMap 10 | } 11 | 12 | func ToMapS[Ele any, Key comparable](elements []Ele, fn func(element Ele) Key) map[Key][]Ele { 13 | resultMap := make(map[Key][]Ele) 14 | for _, element := range elements { 15 | key := fn(element) 16 | resultMap[key] = append(resultMap[key], element) 17 | } 18 | return resultMap 19 | } 20 | --------------------------------------------------------------------------------