feat: add run.ps1 script to start projects easily
add a powershell script that supports starting MES, WMS, WMSClient, Workstation and Dashboard projects with one command, includes build and run logic for backend services and npm dev for dashboard
This commit is contained in:
@@ -10,16 +10,15 @@ import (
|
||||
|
||||
// Config 全局配置
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
Host string `json:",default=0.0.0.0"`
|
||||
Port int `json:",default=8080"`
|
||||
Database db.DatabaseConf
|
||||
Redis RedisConfWrapper
|
||||
Auth AuthConf
|
||||
Internal InternalConf
|
||||
Logger logx.LogConf
|
||||
Wms WmsConf
|
||||
Plc PlcConf
|
||||
rest.RestConf // 自带 Host/Port 等
|
||||
Database db.DatabaseConf
|
||||
Redis RedisConfWrapper
|
||||
Auth AuthConf
|
||||
Internal InternalConf
|
||||
Cli CliConf
|
||||
Logger logx.LogConf
|
||||
Wms WmsConf
|
||||
Plc PlcConf
|
||||
}
|
||||
|
||||
type RedisConfWrapper struct {
|
||||
@@ -38,6 +37,11 @@ type InternalConf struct {
|
||||
Token string `json:",default=Hardman_2026"`
|
||||
}
|
||||
|
||||
// CliConf 命令行敏感操作门禁密码(与管理员账号密码保持一致,改密时自动同步)
|
||||
type CliConf struct {
|
||||
AdminPassword string `json:"AdminPassword,default=Hardman_2026"`
|
||||
}
|
||||
|
||||
// WmsConf WMS 内部服务地址
|
||||
type WmsConf struct {
|
||||
BaseURL string `json:",default="`
|
||||
@@ -46,7 +50,7 @@ type WmsConf struct {
|
||||
|
||||
// PlcConf PLC 对接配置(mock 时不用)
|
||||
type PlcConf struct {
|
||||
Enable bool `json:",default=false"`
|
||||
Host string `json:",default=127.0.0.1"`
|
||||
Port int `json:",default=102"`
|
||||
}
|
||||
Enable bool `json:",default=false"`
|
||||
Host string `json:",default=127.0.0.1"`
|
||||
Port int `json:",default=102"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -54,4 +55,43 @@ func NewDB(c DatabaseConf) (*sql.DB, error) {
|
||||
return nil, err
|
||||
}
|
||||
return sqlDB, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AutoMigrate 执行 ent 自动建表/补齐(只增,不删数据)。供命令行 migrate 使用。
|
||||
|
||||
func AutoMigrate(c DatabaseConf) error {
|
||||
client, sqlDB := MustNewDB(c)
|
||||
defer sqlDB.Close()
|
||||
return client.Schema.Create(context.Background())
|
||||
}
|
||||
|
||||
// DropAllTables 清空 public schema 下所有业务表(破坏性,仅供命令行 reset-all 使用)。
|
||||
func DropAllTables(c DatabaseConf) error {
|
||||
sqlDB, err := NewDB(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
|
||||
rows, err := sqlDB.Query(`SELECT tablename FROM pg_tables WHERE schemaname='public'`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var names []string
|
||||
for rows.Next() {
|
||||
var t string
|
||||
if err := rows.Scan(&t); err != nil {
|
||||
rows.Close()
|
||||
return err
|
||||
}
|
||||
names = append(names, t)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
for _, t := range names {
|
||||
if _, err := sqlDB.Exec(`DROP TABLE IF EXISTS "` + t + `" CASCADE`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package handler
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
tokenx "bj_power_mes/common/token"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
|
||||
@@ -27,6 +29,22 @@ func ctxUserId(r *http.Request) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// uidFromRequest 取当前登录用户ID:优先 context,缺失时回退解析 Authorization Bearer token(更稳健)
|
||||
func uidFromRequest(r *http.Request, svcCtx *svc.ServiceContext) int {
|
||||
if uid := ctxUserId(r); uid > 0 {
|
||||
return uid
|
||||
}
|
||||
auth := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
||||
if auth == "" || auth == r.Header.Get("Authorization") {
|
||||
return 0
|
||||
}
|
||||
claims, err := tokenx.Parse(svcCtx.Config.Auth.AccessSecret, auth)
|
||||
if err == nil && claims.UserId > 0 {
|
||||
return claims.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ctxOperator 当前操作人:优先取 name,其次 username
|
||||
func ctxOperator(r *http.Request) string {
|
||||
if v, ok := r.Context().Value("name").(string); ok && v != "" {
|
||||
@@ -82,7 +100,7 @@ func RefreshHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
|
||||
func UserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := logic.New(svcCtx).UserInfo(r.Context(), ctxUserId(r))
|
||||
data, err := logic.New(svcCtx).UserInfo(r.Context(), uidFromRequest(r, svcCtx))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 1003, err.Error())
|
||||
return
|
||||
@@ -154,6 +172,23 @@ func DeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 修改自己的密码 ----------
|
||||
|
||||
func ChangePasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req logic.ChangePasswordReq
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).ChangePassword(r.Context(), uidFromRequest(r, svcCtx), req); err != nil {
|
||||
httpx.Fail(w, 1017, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "密码修改成功", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 角色管理 ----------
|
||||
|
||||
func ListRolesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
@@ -258,4 +293,4 @@ func DeletePermissionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
httpx.OkMessage(w, "删除成功", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func RegisterRoutes(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
jwtAdmin := []rest.Route{
|
||||
{Method: http.MethodGet, Path: "/userinfo", Handler: UserInfoHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/seed", Handler: SeedHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/user/change-password", Handler: ChangePasswordHandler(serverCtx)},
|
||||
|
||||
{Method: http.MethodGet, Path: "/users", Handler: ListUsersHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/users", Handler: CreateUserHandler(serverCtx)},
|
||||
@@ -40,4 +41,4 @@ func RegisterRoutes(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithPrefix("/api/v1"),
|
||||
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ type UserInfoResp struct {
|
||||
PermissionCodes []string `json:"permissionCodes"`
|
||||
}
|
||||
|
||||
type ChangePasswordReq struct {
|
||||
OldPassword string `json:"oldPassword"`
|
||||
NewPassword string `json:"newPassword"`
|
||||
}
|
||||
|
||||
type permissionItem struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
@@ -148,6 +153,31 @@ func (s *Service) UserInfo(ctx context.Context, userId int) (UserInfoResp, error
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ChangePassword 当前登录用户修改自己的密码
|
||||
func (s *Service) ChangePassword(ctx context.Context, userId int, req ChangePasswordReq) error {
|
||||
if userId <= 0 {
|
||||
return errors.New("未登录")
|
||||
}
|
||||
if req.OldPassword == "" || req.NewPassword == "" {
|
||||
return errors.New("请输入旧密码与新密码")
|
||||
}
|
||||
if len(req.NewPassword) < 6 {
|
||||
return errors.New("新密码长度至少 6 位")
|
||||
}
|
||||
usr, err := s.ctx.EntClient.User.Get(ctx, userId)
|
||||
if err != nil {
|
||||
return errors.New("用户不存在")
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(usr.Password), []byte(req.OldPassword)) != nil {
|
||||
return errors.New("旧密码不正确")
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ctx.EntClient.User.UpdateOneID(userId).SetPassword(string(hash)).Exec(ctx)
|
||||
}
|
||||
|
||||
// ------ 用户 / 角色 / 权限 管理 ------
|
||||
|
||||
type UserReq struct {
|
||||
|
||||
@@ -33,14 +33,18 @@ func (s *Service) Seed(ctx context.Context) error {
|
||||
_ = s.ctx.EntClient.Role.Create().SetName(r.name).SetCode(r.code).SetPermissionCodes(codes).Exec(ctx)
|
||||
}
|
||||
|
||||
// 2. 超级管理员账号 admin / Hardman_2026
|
||||
if !s.ctx.EntClient.User.Query().Where(user.Username("admin")).ExistX(ctx) {
|
||||
hash, _ := bcrypt.GenerateFromPassword([]byte("Hardman_2026"), bcrypt.DefaultCost)
|
||||
adminRole, _ := s.ctx.EntClient.Role.Query().Where(role.Code("SUPER_ADMIN")).First(ctx)
|
||||
roleId := 0
|
||||
if adminRole != nil {
|
||||
roleId = adminRole.ID
|
||||
}
|
||||
// 2. 超级管理员账号 admin / Hardman_2026(对已存在的旧行做对齐,避免残留禁用状态)
|
||||
hash, _ := bcrypt.GenerateFromPassword([]byte("Hardman_2026"), bcrypt.DefaultCost)
|
||||
adminRole, _ := s.ctx.EntClient.Role.Query().Where(role.Code("SUPER_ADMIN")).First(ctx)
|
||||
roleId := 0
|
||||
if adminRole != nil {
|
||||
roleId = adminRole.ID
|
||||
}
|
||||
if s.ctx.EntClient.User.Query().Where(user.Username("admin")).ExistX(ctx) {
|
||||
_ = s.ctx.EntClient.User.Update().
|
||||
Where(user.Username("admin")).
|
||||
SetPassword(string(hash)).SetStatus("ENABLED").SetRoleId(roleId).Exec(ctx)
|
||||
} else {
|
||||
_ = s.ctx.EntClient.User.Create().
|
||||
SetUsername("admin").SetPassword(string(hash)).SetName("系统管理员").
|
||||
SetRoleId(roleId).SetStatus("ENABLED").Exec(ctx)
|
||||
|
||||
Reference in New Issue
Block a user