feat: support basic user system

This commit is contained in:
xkm
2026-04-07 21:21:18 +08:00
parent deaa14f9f6
commit 1aecb4424c
22 changed files with 654 additions and 65 deletions

44
internal/service/auth.go Normal file
View File

@@ -0,0 +1,44 @@
package service
import (
"context"
"database/sql"
"errors"
"time"
"github.com/go-chi/jwtauth/v5"
"golang.org/x/crypto/bcrypt"
)
func (s *Service) VerifyUser(ctx context.Context, input VerifyUserInput) (VerifyUserResult, error) {
u, err := s.queries.GetUserByUsername(ctx, input.Username)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return VerifyUserResult{}, notFound("user does not exist or password is wrong")
}
return VerifyUserResult{}, internalError("auth error", nil)
}
if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(input.Password)); err != nil {
return VerifyUserResult{}, notFound("user does not exist or password is wrong")
}
// jwt
claims := make(map[string]any)
claims["userid"] = u.ID.String()
claims["username"] = u.Username
claims["role"] = u.Role
claims["account_status"] = u.AccountStatus
jwtauth.SetExpiryIn(claims, 24*time.Hour)
jwtauth.SetIssuedNow(claims)
_, token, _ := s.config.JWTAuth.Encode(claims)
return VerifyUserResult{
Token: token,
UserId: u.ID.String(),
UserName: u.Username,
DisplayName: u.DisplayName,
Role: u.Role,
}, nil
}

View File

@@ -0,0 +1,13 @@
package service
import (
"log"
"testing"
"golang.org/x/crypto/bcrypt"
)
func TestGenerateHashedPassword(t *testing.T) {
e, _ := bcrypt.GenerateFromPassword([]byte("1145141919810"), bcrypt.DefaultCost)
log.Println(string(e))
}

View File

@@ -0,0 +1 @@
package service

View File

@@ -2,13 +2,16 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"math"
"strconv"
"strings"
"time"
"gitea.starryskymeow.cn/B309/datamarket/internal/config"
"gitea.starryskymeow.cn/B309/datamarket/internal/repository"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
@@ -37,12 +40,14 @@ var (
type Service struct {
queries *repository.Queries
config *config.Config
now func() time.Time
}
func New(queries *repository.Queries) *Service {
func New(queries *repository.Queries, cfg *config.Config) *Service {
return &Service{
queries: queries,
config: cfg,
now: time.Now,
}
}
@@ -884,29 +889,33 @@ func timestamptzValue(value time.Time) pgtype.Timestamptz {
func toAsset(asset repository.DataAsset) Asset {
return Asset{
ID: asset.ID.String(),
AssetName: asset.AssetName,
AssetType: asset.AssetType,
Domain: asset.Domain,
ApplicationScene: toTextPointer(asset.ApplicationScene),
DataDescription: asset.DataDescription,
DataScale: asset.DataScale,
CollectionMethod: asset.CollectionMethod,
LabelingStatus: toTextPointer(asset.LabelingStatus),
UpdateFrequency: toTextPointer(asset.UpdateFrequency),
PrivacyLevel: asset.PrivacyLevel,
PermissionMode: asset.PermissionMode,
SupportsValidation: asset.SupportsValidation,
SellerExpectedPriceMin: toNumericPointer(asset.SellerExpectedPriceMin),
SellerExpectedPriceMax: toNumericPointer(asset.SellerExpectedPriceMax),
QualityLevel: toTextPointer(asset.QualityLevel),
ScarcityLevel: toTextPointer(asset.ScarcityLevel),
BaseValueScore: toNumericPointer(asset.BaseValueScore),
BasePriceMin: toNumericPointer(asset.BasePriceMin),
BasePriceMax: toNumericPointer(asset.BasePriceMax),
AssetStatus: asset.AssetStatus,
CreatedAt: toTimePointer(asset.CreatedAt),
UpdatedAt: toTimePointer(asset.UpdatedAt),
ID: asset.ID.String(),
AssetName: asset.AssetName,
AssetType: asset.AssetType,
Domain: asset.Domain,
ApplicationScene: toTextPointer(asset.ApplicationScene),
DataDescription: asset.DataDescription,
DataScale: asset.DataScale,
CollectionMethod: asset.CollectionMethod,
LabelingStatus: toTextPointer(asset.LabelingStatus),
UpdateFrequency: toTextPointer(asset.UpdateFrequency),
PrivacyLevel: asset.PrivacyLevel,
PermissionMode: asset.PermissionMode,
SupportsValidation: asset.SupportsValidation,
SellerExpectedPriceMin: toNumericPointer(asset.SellerExpectedPriceMin),
SellerExpectedPriceMax: toNumericPointer(asset.SellerExpectedPriceMax),
QualityLevel: toTextPointer(asset.QualityLevel),
ScarcityLevel: toTextPointer(asset.ScarcityLevel),
BaseValueScore: toNumericPointer(asset.BaseValueScore),
BasePriceMin: toNumericPointer(asset.BasePriceMin),
BasePriceMax: toNumericPointer(asset.BasePriceMax),
AssetStatus: asset.AssetStatus,
CreatedAt: toTimePointer(asset.CreatedAt),
UpdatedAt: toTimePointer(asset.UpdatedAt),
AgentAssetSummary: toTextPointer(asset.AgentAssetSummary),
AgentRecommendedTasks: toStringArray(asset.AgentRecommendedTasks),
AgentRiskPerMissionAdvice: toTextPointer(asset.AgentRiskPerMissionAdvice),
AgentAssetExplanation: toTextPointer(asset.AgentAssetExplanation),
}
}
@@ -979,6 +988,14 @@ func toTextPointer(value pgtype.Text) *string {
return new(value.String)
}
func toStringArray(value []byte) []string {
var result []string
if err := json.Unmarshal(value, &result); err != nil {
slog.Warn(err.Error(), "value", string(value))
}
return result
}
func toNumericPointer(value pgtype.Numeric) *float64 {
if !value.Valid {
return nil

View File

@@ -36,6 +36,14 @@ type Asset struct {
AssetStatus string `json:"asset_status"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// 智能助手对当前资产的简短画像总结
AgentAssetSummary *string `json:"agent_asset_summary"`
// 推荐的适用任务列表
AgentRecommendedTasks []string `json:"agent_recommended_tasks"`
// 当前资产更适合的权限与风险建议
AgentRiskPerMissionAdvice *string `json:"agent_risk_per_mission_advice"`
// 为什么该资产当前基础价值较高/中/低
AgentAssetExplanation *string `json:"agent_asset_explanation"`
}
type AssetCreateInput struct {
@@ -176,17 +184,20 @@ type AssetService interface {
GetAsset(context.Context, string) (Asset, error)
ListAssets(context.Context, AssetListInput) (ListResult[Asset], error)
UpdateAssetStatus(context.Context, string, StatusUpdate) (AssetStatusResult, error)
//DeleteAsset(context.Context, string) (AssetStatusResult, error)
}
type PricingService interface {
CreatePricing(context.Context, PricingCreateInput) (Pricing, error)
GetPricing(context.Context, string) (Pricing, error)
//DeletePricing(context.Context, string) (Pricing, error)
}
type ValidationService interface {
CreateValidation(context.Context, ValidationCreateInput) (ValidationCreateResult, error)
GetValidation(context.Context, string) (Validation, error)
ListValidations(context.Context, ValidationListInput) (ListResult[Validation], error)
//DeleteValidation(context.Context, string) (Validation, error)
}
type OrderService interface {
@@ -194,4 +205,22 @@ type OrderService interface {
GetOrder(context.Context, string) (Order, error)
ListOrders(context.Context, OrderListInput) (ListResult[Order], error)
UpdateOrderStatus(context.Context, string, StatusUpdate) (OrderCreateResult, error)
//DeleteOrder(context.Context, string) (Order, error)
}
type VerifyUserInput struct {
Username string
Password string
}
type VerifyUserResult struct {
Token string `json:"token"`
UserId string `json:"user_id"`
UserName string `json:"user_name"`
DisplayName string `json:"display_name"`
Role string `json:"role"`
}
type AuthService interface {
VerifyUser(context.Context, VerifyUserInput) (VerifyUserResult, error)
}