Files

562 lines
15 KiB
Go

package service
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
)
func (s *Service) adminState(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
writeJSON(w, http.StatusOK, s.store.Snapshot())
}
func (s *Service) adminAppIDs(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
var cred Credential
if err := json.NewDecoder(r.Body).Decode(&cred); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
if cred.AppID == "" || cred.Password == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "appid and password are required"})
return
}
if cred.Name == "" {
cred.Name = cred.AppID
}
cred.Enabled = true
cred.CreatedAt = time.Now().Format("2006-01-02 15:04:05")
err := s.store.SaveMutation(func(st *Store) error {
for i := range st.AppIDs {
if st.AppIDs[i].AppID == cred.AppID {
st.AppIDs[i].Password = cred.Password
st.AppIDs[i].Name = cred.Name
st.AppIDs[i].Enabled = cred.Enabled
st.AppIDs[i].LastToken = ""
st.AppIDs[i].TokenExpires = ""
delete(st.Tokens, cred.AppID)
return nil
}
}
st.AppIDs = append(st.AppIDs, cred)
return nil
})
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, s.store.Snapshot())
case http.MethodDelete:
appid := r.URL.Query().Get("appid")
_ = s.store.SaveMutation(func(st *Store) error {
next := st.AppIDs[:0]
for _, c := range st.AppIDs {
if c.AppID != appid {
next = append(next, c)
}
}
st.AppIDs = next
delete(st.Tokens, appid)
return nil
})
writeJSON(w, http.StatusOK, s.store.Snapshot())
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (s *Service) adminCards(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
page, pageSize := pagination(r)
filter, err := cardFilterFromRequest(r)
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, s.store.QueryCardsByGroup(filter.keyword, filter.exportStatus, filter.groupID, filter.filterGroup, page, pageSize))
case http.MethodPost:
var card SIMCard
if err := json.NewDecoder(r.Body).Decode(&card); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
normalizeCard(&card)
err := s.store.SaveMutation(func(st *Store) error {
for i := range st.Cards {
if st.Cards[i].MSISDN == card.MSISDN {
st.Cards[i] = card
return nil
}
}
st.Cards = append(st.Cards, card)
return nil
})
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, s.store.Snapshot())
case http.MethodDelete:
msisdn := r.URL.Query().Get("msisdn")
_ = s.store.SaveMutation(func(st *Store) error {
next := st.Cards[:0]
for _, c := range st.Cards {
if c.MSISDN != msisdn {
next = append(next, c)
}
}
st.Cards = next
return nil
})
writeJSON(w, http.StatusOK, s.store.Snapshot())
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (s *Service) adminCardGroups(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
st := s.store.Snapshot()
counts := map[int64]int{}
for _, card := range st.Cards {
counts[card.GroupID]++
}
for i := range st.Groups {
st.Groups[i].CardCount = counts[st.Groups[i].ID]
}
writeJSON(w, http.StatusOK, st.Groups)
case http.MethodPost:
var input struct {
Name string `json:"name"`
}
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
input.Name = strings.TrimSpace(input.Name)
if input.Name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "group name is required"})
return
}
var created CardGroup
err := s.store.SaveMutation(func(st *Store) error {
for _, group := range st.Groups {
if strings.EqualFold(group.Name, input.Name) {
return fmt.Errorf("group name already exists")
}
}
id := int64(1)
for _, group := range st.Groups {
if group.ID >= id {
id = group.ID + 1
}
}
now := time.Now().Format("2006-01-02 15:04:05")
created = CardGroup{ID: id, Name: input.Name, CreatedAt: now, UpdatedAt: now}
st.Groups = append(st.Groups, created)
return nil
})
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, created)
case http.MethodDelete:
id, err := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
if err != nil || id <= 0 {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid group id"})
return
}
err = s.store.SaveMutation(func(st *Store) error {
found := false
next := st.Groups[:0]
for _, group := range st.Groups {
if group.ID == id {
found = true
} else {
next = append(next, group)
}
}
if !found {
return fmt.Errorf("card group not found")
}
st.Groups = next
for i := range st.Cards {
if st.Cards[i].GroupID == id {
st.Cards[i].GroupID = 0
st.Cards[i].GroupName = ""
}
}
return nil
})
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, s.store.Snapshot())
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (s *Service) adminSettings(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
var settings SimulationSettings
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
_ = s.store.SaveMutation(func(st *Store) error {
st.Settings = settings
return nil
})
writeJSON(w, http.StatusOK, s.store.Snapshot())
}
func (s *Service) adminOrders(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
page, pageSize := pagination(r)
writeJSON(w, http.StatusOK, s.store.QueryOrders(page, pageSize))
return
}
if r.Method != http.MethodDelete {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
_ = s.store.SaveMutation(func(st *Store) error {
st.Orders = []Order{}
return nil
})
writeJSON(w, http.StatusOK, s.store.Snapshot())
}
func pagination(r *http.Request) (int, int) {
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
pageSize, _ := strconv.Atoi(r.URL.Query().Get("pageSize"))
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 20
}
if pageSize > 100 {
pageSize = 100
}
return page, pageSize
}
type cardFilter struct {
keyword string
exportStatus string
groupID int64
filterGroup bool
}
func cardFilterFromRequest(r *http.Request) (cardFilter, error) {
filter := cardFilter{
keyword: r.URL.Query().Get("keyword"),
exportStatus: strings.TrimSpace(r.URL.Query().Get("exportStatus")),
}
if filter.exportStatus != "" && filter.exportStatus != "exported" && filter.exportStatus != "not_exported" {
return cardFilter{}, fmt.Errorf("invalid export status")
}
groupRaw := strings.TrimSpace(r.URL.Query().Get("groupId"))
if groupRaw == "" {
return filter, nil
}
groupID, err := strconv.ParseInt(groupRaw, 10, 64)
if err != nil || groupID < 0 {
return cardFilter{}, fmt.Errorf("invalid group id")
}
filter.groupID = groupID
filter.filterGroup = true
return filter, nil
}
func (s *Service) adminGenerate(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
count, groupID, err := generationRequest(r)
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
result, err := s.store.GenerateCardsInGroup(count, groupID)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, result)
}
func (s *Service) adminSync(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
count, _, err := generationRequest(r)
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
result, err := s.store.GenerateCards(count)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, result)
}
func (s *Service) adminCardsExport(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
var cards []SIMCard
if r.URL.Query().Get("selection") == "filtered" {
filter, err := cardFilterFromRequest(r)
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
cards = s.store.QueryAllCardsByGroup(filter.keyword, filter.exportStatus, filter.groupID, filter.filterGroup)
} else {
cards = exportCardsByMSISDN(s.store.Snapshot().Cards, r.URL.Query().Get("msisdns"))
}
raw, err := exportInboundCardsXLSX(cards)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
exportedAt := time.Now().Format("2006-01-02 15:04:05")
exportedMSISDNs := make(map[string]struct{}, len(cards))
for _, card := range cards {
exportedMSISDNs[card.MSISDN] = struct{}{}
}
if err := s.store.SaveMutation(func(st *Store) error {
for i := range st.Cards {
if _, ok := exportedMSISDNs[st.Cards[i].MSISDN]; ok {
st.Cards[i].Exported = true
st.Cards[i].ExportCheckedAt = exportedAt
}
}
return nil
}); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
w.Header().Set("Content-Disposition", `attachment; filename="sim_card_export.xlsx"`)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(raw)
}
func exportCardsByMSISDN(cards []SIMCard, rawMSISDNs string) []SIMCard {
rawMSISDNs = strings.TrimSpace(rawMSISDNs)
if rawMSISDNs == "" {
return cards
}
selected := map[string]bool{}
for _, item := range strings.FieldsFunc(rawMSISDNs, func(r rune) bool {
return r == ',' || r == '_' || r == ' '
}) {
item = strings.TrimSpace(item)
if item != "" {
selected[item] = true
}
}
if len(selected) == 0 {
return cards
}
out := make([]SIMCard, 0, len(selected))
for _, card := range cards {
if selected[card.MSISDN] {
out = append(out, card)
}
}
return out
}
func generationCount(r *http.Request) (int, error) {
count := 100
if r.Body != nil {
var payload struct {
Count int `json:"count"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err == nil && payload.Count > 0 {
count = payload.Count
}
}
if value := r.URL.Query().Get("count"); value != "" {
parsed, err := strconv.Atoi(value)
if err != nil {
return 0, err
}
count = parsed
}
if count <= 0 {
return 0, strconv.ErrSyntax
}
return count, nil
}
func generationRequest(r *http.Request) (int, int64, error) {
var input struct {
Count int `json:"count"`
GroupID *int64 `json:"groupId"`
}
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
if err == io.EOF {
count, countErr := generationCount(r)
return count, 0, countErr
}
return 0, 0, err
}
if input.Count <= 0 {
return 0, 0, fmt.Errorf("count must be greater than 0")
}
if input.GroupID == nil || *input.GroupID < 0 {
return input.Count, 0, nil
}
return input.Count, *input.GroupID, nil
}
func normalizeCard(card *SIMCard) {
now := time.Now()
card.MSISDN = strings.TrimSpace(card.MSISDN)
card.ICCID = strings.TrimSpace(card.ICCID)
card.IMSI = strings.TrimSpace(card.IMSI)
if card.CardStatus == "" {
card.CardStatus = "2"
}
if card.StopReason == "" {
card.StopReason = defaultStopReason(card.CardStatus)
}
if card.FrozenStatus == "" {
card.FrozenStatus = "0"
}
if card.FrozenOperTime == "" {
card.FrozenOperTime = diagnosticTime(now)
}
if card.LastChangeDate == "" {
card.LastChangeDate = now.Format("2006-01-02 15:04:05")
}
if card.OpenDate == "" {
card.OpenDate = now.Format("2006-01-02 15:04:05")
}
if card.ActiveDate == "" {
card.ActiveDate = now.Format("2006-01-02 15:04:05")
}
if card.OnOffStatus == "" {
card.OnOffStatus = "1"
}
if card.SessionStatus == "" {
card.SessionStatus = "01"
}
if card.SessionStatus != "00" && card.SessionStatus != "01" {
card.SessionStatus = "00"
}
if card.SessionCreateDate == "" {
card.SessionCreateDate = card.LastChangeDate
}
if card.SessionCreateDate == "" {
card.SessionCreateDate = card.LastChangeDate
}
if card.RAT == "" {
card.RAT = "6"
}
if card.CardBindStatus == "" {
card.CardBindStatus = "1"
}
if card.RegionLimitStatus == "" {
card.RegionLimitStatus = "0"
}
if card.AreaRestrictType == "" {
card.AreaRestrictType = "1"
}
if card.RestrictedArea == "" {
card.RestrictedArea = "200"
}
if card.LastUsageUpdateTime == "" {
card.LastUsageUpdateTime = now.Format(time.RFC3339)
}
card.GrowthPerMinuteKB = 0
if len(card.APNs) == 0 {
card.APNs = []APN{{Name: "CMIOT", Status: "1", UsageBytes: card.MonthUsageKB * 1024}}
}
for i := range card.APNs {
apn := &card.APNs[i]
if apn.Name == "" {
apn.Name = "CMIOT"
}
if apn.Status == "" {
apn.Status = "1"
}
if apn.DiagnosisStatus == "" {
apn.DiagnosisStatus = diagnosisStatus(apn.Status)
}
if apn.ModifyTime == "" {
apn.ModifyTime = diagnosticTime(now)
}
if apn.ServiceUsageState == "" {
apn.ServiceUsageState = "1"
}
if len(apn.PCCs) == 0 {
apn.PCCs = []PCC{{ServiceCode: "default", Name: "APN 缺省流量", PolicyStatus: "2", ServiceUsageState: "1", ModifyTime: apn.ModifyTime}}
}
for j := range apn.PCCs {
if apn.PCCs[j].ServiceCode == "" {
apn.PCCs[j].ServiceCode = "default"
}
if apn.PCCs[j].Name == "" {
apn.PCCs[j].Name = "APN 缺省流量"
}
if apn.PCCs[j].PolicyStatus == "" {
apn.PCCs[j].PolicyStatus = "2"
}
if apn.PCCs[j].ServiceUsageState == "" {
apn.PCCs[j].ServiceUsageState = "1"
}
if apn.PCCs[j].ModifyTime == "" {
apn.PCCs[j].ModifyTime = apn.ModifyTime
}
}
}
}
func diagnosticTime(value time.Time) string { return value.Format("20060102150405") }
func defaultStopReason(cardStatus string) string {
if cardStatus == "4" {
return "000020000000"
}
return "000000000000"
}
func diagnosisStatus(status string) string {
switch status {
case "0":
return "4"
case "1":
return "2"
default:
return status
}
}