init project

This commit is contained in:
ping 2025-10-27 15:50:44 +08:00
commit 5fe6572615
1500 changed files with 155923 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea/
.DS_Store
*.tar
*.tar.gz
*.rar
__pycache__/

View File

@ -0,0 +1,81 @@
# !/d/ymq/py3/bin/python
import asyncio
import getpass
import os
import re
import subprocess
from appPublic.sshx import SSHNode
home = os.getenv('HOME')
kgadget_build = False
class KbossAppDeploy:
def __init__(self, node):
self.node = node
async def update_code(self):
await self.node._l2r("/root/backup/www/www_backup", '~/')
await self.node._run(f'cd ~')
await self.node._run(f'tar xf ./www_backup/kboss.prod.tar.back')
await self.node._run('chmod +x bin/web')
await self.node._run('tar xzf ./www_backup/files.tar.gz')
await self.node._run(f'cp -r ./files/ ~/www/ ')
try:
await self.node._run(f'tar zxf ./www_backup/kboss_nginx.tar.gz')
await self.node._run('sudo cp ./kboss_nginx/*kaiyuancloud.* /etc/nginx/')
await self.node._run(f'sudo cp ./kboss_nginx/kbossprod /etc/nginx/sites-enabled/')
except Exception as e:
print(f"set nginx config fail :{e}")
await self.node._run(f'cd ~')
await self.node._run('bin/web')
print("deploy ok")
async def init_data(self):
await self.node._run(f'mysql -h db -ukboss -pkboss123 kboss_prod < ~/initdata/mysql.ddl.sql')
await self.node._run(f'mysql -h db -ukboss -pkboss123 kboss_prod < ~/initdata/kboss_init.sql')
async def get_remote_data(self):
subprocess.run("tar zcf /root/backup/www/www_backup/files.tar.gz files/", shell=True, capture_output=True, text=True)
async def deploy(self):
await self.get_remote_data()
await self.node.connect()
await self.update_code()
self.node.close()
def default_node(host, user, port):
node = SSHNode(host,
username=user,
port=port,
password=getpass.getpass(f'{user}@{host} password:')
)
return node
if __name__ == '__main__':
import sys
exp = f"python3 {sys.argv[0]} tt@192.168.0.11:10022"
if len(sys.argv) != 2:
print(f'Usage:\n{exp}')
sys.exit(1)
if re.findall(r'^[\w\d]+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$', sys.argv[1]):
user_ip_prod = sys.argv[1]
user, host = user_ip_prod.split('@')
host, port = host.split(':')
if not port:
port = 22
port = int(port)
else:
print(f'ip not defined ip:{sys.argv[1]}')
sys.exit(1)
node = default_node(host, user, port)
if not node:
sys.exit(1)
d = KbossAppDeploy(node)
loop = asyncio.get_event_loop()
loop.run_until_complete(d.deploy())

55
DevOps/kboss_domain.py Normal file
View File

@ -0,0 +1,55 @@
import asyncio
from pyppeteer import launch
async def main(mode):
browser = await launch(headless=False, args=['--disable-blink-features=AutomationControlled'])
page = await browser.newPage()
await page.setViewport({'width': 1020, 'height': 1080})
await page.waitFor(1000)
url = "https://signin.aliyun.com/login.htm#/main"
await page.goto(url)
await page.waitFor(2000)
await page.type('#loginName', 'www_domain@1953083695298848.onaliyun.com')
await page.keyboard.press('Enter')
await page.waitFor(3000)
await page.screenshot({'path': 'screenshot.png'})
await page.type('#loginPassword', 'Kyy@123456789')
await page.keyboard.press('Enter')
await page.waitFor(3000)
await page.screenshot({'path': 'screenshot1.png'})
url = "https://dns.console.aliyun.com/#/dns/setting/kaiyuancloud.cn"
await page.goto(url)
await page.waitFor(3000)
await page.waitForSelector('#keyword')
await page.type('#keyword', mode)
await page.keyboard.press('Enter')
await page.waitFor(1000)
element = await page.querySelector(
'#app-common-page > div > div._1_aZzbvH > div > div.ant-tabs-content.ant-tabs-content-no-animated.ant-tabs-top-content.ant-tabs-card-content > div.ant-tabs-tabpane.ant-tabs-tabpane-active > div:nth-child(2) > div > div > div:nth-child(2) > div.ant-table-wrapper._3PHtW8UK > div > div > div > div > div.ant-table-fixed-right > div > div > table > tbody > tr:nth-child(1) > td > span > span:nth-child(3)'
)
if element:
await element.click()
else:
await page.waitFor(1000)
element = await page.querySelector(
'#app-common-page > div > div._1_aZzbvH > div > div.ant-tabs-content.ant-tabs-content-no-animated.ant-tabs-top-content.ant-tabs-card-content > div.ant-tabs-tabpane.ant-tabs-tabpane-active > div:nth-child(2) > div > div > div:nth-child(2) > div.ant-table-wrapper._3PHtW8UK > div > div > div > div > div.ant-table-fixed-right > div > div > table > tbody > tr:nth-child(4) > td > span > span:nth-child(3)'
)
if element:
await element.click()
await page.screenshot({'path': 'screenshot.png'})
await page.waitFor(2000)
url = "https://account.aliyun.com/logout/logout.htm?spm=a2c1d.8251892.top-nav.dsign-out.2b3c5b76Cv4sYC&oauth_callback=https%3A%2F%2Fdns.console.aliyun.com%2F%3Fspm%3D5176.100251.111252.28.d2344f156kVH5u%26accounttraceid%3D61009978d16c4f1091b041d678f33d56mntp%23%2Fdns%2Fsetting%2Fkaiyuancloud.cn"
await page.goto(url)
await page.waitFor(1000)
await browser.close()
mode = '@'
asyncio.get_event_loop().run_until_complete(main(mode=mode))

Binary file not shown.

7
DevOps/m.files.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
SOURCE_DIR="/home/kbossprod/www/files/"
DEST_DIR="root@192.168.0.2:/root/backup/www/files/"
inotifywait -m -r -e create --format "%w%f" "$SOURCE_DIR" | while read NEW_FILE
do
rsync -avz --relative -e "ssh -p 10022" "$NEW_FILE" "$DEST_DIR"
done

11
DevOps/m.sh Normal file
View File

@ -0,0 +1,11 @@
#!/bin/bash
source_dir="/home/kbossprod/kboss.prod.tar.back"
previous_state=$(ls -l $source_dir)
while true; do
sleep 30
current_state=$(ls -l $source_dir)
if [ "$previous_state" != "$current_state" ]; then
scp -P 10022 $source_dir root@192.168.0.2:/root/backup/www
previous_state=$current_state
fi
done

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# Kboss
kaiyuan business operator support system
## Dependent
* [bricks](https://github.com/yumoqing/bricks) use as kboss frontend development framework
* [ahserver](https://github.com/yumoqing/ahserver) as kboss backend application server
* [sqlor](https://github.com/yumoqing/sqlor) used by ahserver for databases operation
* [aiohttp](https://github.com/aio-libs/aiohttp) use as web server.
##

1
alipay/dev/appid.txt Normal file
View File

@ -0,0 +1 @@
2021004106675236

1
alipay/dev/huidiao.txt Normal file
View File

@ -0,0 +1 @@
https://www.kaiyuancloud.cn/dev/pay/payhuidiao.dspy

3
alipay/dev/private.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLODFiJDK+3UB2/zELdFuCYzx3diDMLr8pj/yWz01kYxjw96k8yAIRXFBJDxc/PIEQBifASS4irV1TVtbns+L7ANtDnYtFe8ewO+875rJagqq1tIS6IthiWlGg6rmbzrTtYLSNoCy2rt2hoZyhvE9vm96cjTBc+o+WzIsw1Vex/vahSk0JnviX1kiw9vddAUtupuyLcHIV0VOH04MRfT+C8KsDUZNEmCwlGI/DFmO6DFpniy+jlNb3U8yJqD3FTJhky4I90uYvYRvFeGQA7beZwFnL+oOuspk+gBFZBXyPonHMgxR9Olk6gUkXT3nFibnKqtltZIS82oes24dRLZJfAgMBAAECggEAYui0ZRZ0mxeT8jcRwCKV1Qft8K2TVZ0soTmz6e0z/ctN7/z0VsN+fJkKTS2UhY/V3e2RllInnelvcYMzwYATFeMg5GPsi5wKXhHmwLcBJb7Jy7iJx7hz5URn1ByPa7uq3S8kd65BtjR8L5YjMuEXyqKBwNWqc5RsKWX+yd34pPMEQ0qBeOxtAk3ugVhY7WgJu1GcUhseJu/oUC45ARge2AkMz7m7Fi+xxyfDYklcn10s8d8ripRXFBc2Tmg0oHBldjrnNK+o2wqOV/KadmegE5fLntDx4bIe8bXPEZMXWWhWrXG2WpbEBMkBe1GsqIo5XWbEmzbi/xJM68KxeHyk+QKBgQC9EFK+eTQlt5VQ89WSe1s5W2jOs916ILCSKR6a1QesjAk6sUJxh/g9vwm/WhQC8fOXo9oHycqBg860TlbDNS/nX+PoUMWp3LzwTfaboaDOOCQ3DPCrxztHLSObLyYnAHYEU5daLzM2AsBClmqSWv+6OOvQJJ5dCx1be7jcRaqXOwKBgQC8gkQlyexShdkJeAZbrGH4NyCVGUv2wM01mcPW08YHb8xbCzv50Kqvk9Ulej3vRgccKKd3M51RE+1e4p0UDTeYM9crnPMfM2BgFNv0sWM1pSZp0rlpkxG7pPz8t8/muuQjJjy+is82YDhpO3OU2VxWKt6OpG/Mu59+Wy0vUwUnLQKBgQC6lHVUDADuh/IPZdMLMDmirRcNhmu3rrMSSwos+rcMDVa+WVic+fZ92R4hfR/qmCQxLj5pGTeEATPotLbjBoYz7GnaRnwLWhALhqUsiaFMYhM7UMXigEd7wow1BZ69NrNBKc1f7ty3HJfoHtElhjCA8tOlIb43TFS4h6yzlPz5KQKBgFe7dMX1jRP3EUSz5Jmjx9DCr1pU9KK5pofssVV5KC/r2zsAJoCkmduvPML9eneyqrpzPUVf8zZ5xL6lTx+26wneDqVQnWIHAjKVYq0mJZsg6pjTptE4zkb7iXaAgbTLhEPLlvfDGJ8g9wAa2DcNVkkQGJZgd1vccmVXP9dHlx1dAoGBAIayI4cBHVqO9frmRfH64T1Ztxi4OvmJvXlZdocjdwdBNtQEDQ6r6pQR4cDhSBHj9BPo51Qc3rCu4dcxkl78YcbNjUzBzts8KpGwtOavzON36mUG7NUhHDcXqnM7fQWuWsM3WZhZH2y1UeUVsXOIKenqDluVkh9BVEwgZjMEYpE7
-----END RSA PRIVATE KEY-----

3
alipay/dev/public.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAogRDEu+GlU0KEZfcWS7P2hcsKLB1/shRXw2AF2bozuc1rrfl2noufAEP7ULMd9kw66C8fJd8H/tPIfff2expmQGa5dJBELZqKlMdTtxxa7t5+OGFWZsqzBlCPci9XjVwWfxamLw5IZTX5qocTUkCgFd62y/fpMsQfQPmU1vAdUSWp7CpS0E/ljks1Gl44hv5UOBZdOxEY6/ww+R3mPosycFk3kaMxe4P002kq7gfcNmZdRm8O9i+q0KsG+LF+Q2qDWQ/xO1UF8R4au50DijGk2S2X3bJk1tjytaDTNHZmcJGWP5qNuHZ82KAT42b0iHAg8HO7XqAmCp9bskX+3gSMwIDAQAB
-----END PUBLIC KEY-----

1
alipay/prod/appid.txt Normal file
View File

@ -0,0 +1 @@
2021004120652840

1
alipay/prod/huidiao.txt Normal file
View File

@ -0,0 +1 @@
https://www.opencomputing.cn/pay/payhuidiao.dspy

3
alipay/prod/private.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXl2cIrTPP07WqtIdGlskI3G/UCe2APu13MuJQmyk8RK+VlRFv25JTboa24Hdr7gKbatj4+RzTP/EKxOtRVuNeTijLFvRJf3dEgJzWwDa0zU/J40AmGiPGeqJID6mAKFaI2ugRMO/HO7X6fZxGP+fMn0qSErnXtMyveYSRhgrV5HF0Hpbp+6a5vT3ifOXy5fzzlGKLkmlIw6XolRl2xF4/jl7o9BNjQTcBTY50ENyByMXRckX5McTh/fsOsrMmw8CBiJRWf0Vm/01Gt9pqddh1gIlBKb6M8ZSt52lAGMI7phe1ka54hfu4Z2Ofj3WZTUWy1lz8P5zn7oUxrZ8rJAC1AgMBAAECggEAYt6knMwaltoGCqhj8m42AEmFxk4TUSm5x5ywbtJEOsKxomXvUX1sGm5j/rK4MPSzTpqJmRkg68cwd02oDQ0WjlN717nI6obwSwV3Rbln9NRTzeEh3bf+zVz+qvFMJAIrLmdYJJ4+RTt0nUN/wXG8xYC/KjZ8b2vEdE5VYHSEbKvojg1/yX1L4AGmTYXNtRmPyEfWf7tYm5h4aj1j1bkwY/A6VcABTcs1hmD+ab6ejCnEjtBlCLAycEQ6nwktBtwpcZhrzdBOj80ZeoBZEmi0ZajBBwFDjRaKO7QZ2mYcnOczochqdnKuT6OAWtsOyEyCIQsfIXEArIeZiPPk5w3kAQKBgQDTzmC2+IwbhLimJKQvDzmrfs6qXGA+qXfBOTSaEsiInPQdOIqXTOW8uim0+qN7zHoshks8FvnRs4rpikhtPEFRGhy8skynPtJbs0PDm165dtrvlC1JxyRoEk9D+h1NPA33rP9aIWzHomi0TRYWovSKviS2iw/TTShEAvVItnJf1QKBgQC3OKe0NFcHLLIk8lWzU44CAU3M1mbecljzFzfIcIHDWXu/CsMiylcMvSi1PE7Osz6v8/gDQ+HUzT3eFj1zMZsDRwVS4nXQUkTww4wevvsZWct1pX97ut1femaFrwITMInkR6Yj2+IG49GlgoK0RoiUvF1SbEx5jnWIVIn4ewBtYQKBgQCb6UUEKv1nnFEX68z33ytCdDvI5+ro9XJzx5pS+BSpi2+euuo56Z8LR7Y/xGfnT2N3tvtWpksK9DEDLImX70zdWG1i8ZS/X90VJ2ZwbMRTnYwX42la77565U936Hr9SYG2mZ/2Rrh+U6zjXAMwco+0ObdosADcN+W1+r934OVcaQKBgQCoKejKnetum1haTs3XzI2fS4ETy4kZDAyV+S5q2tBO8veADamyadJhz8/oIBUY5C1aI2rz3D8PKN1q1DDMZLAo3uuE+nM1o/zAmkomAhXHW8JBFYgfkRATzO0CL/GQmVFGY3iamvsmZb0K0vGR8tby5LwmpV2/h8BaQ3VqycJHoQKBgGpXBBt1cvKzGe/Ot25jYiZrZ4niPy8R1ENzv8+rTIbO396auCk2h7tsyNOph1ZrNz5W+qr9yLQHqimz2OvkKGxlzSa6o7xDQ0mSeng+hWO0cJcXOhzQh3jjE2fRZUmgTCWISjFLVp78/K9MFSofk5/+hoqYniIEeaSjX+cHn/D1
-----END RSA PRIVATE KEY-----

3
alipay/prod/public.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgD4YlfnWKd4vEHsim6vxLwustbnBBh9IUJwF5rGJ3b7wjYyzMnQZ36Cgf81A685IQ+Ni9GogNDaUWZx9V+qGxZRwaLbktSLnUNwPMudKlUoPyQtqyygU+Bmwg1B+UBzZsz8eG72qOuvu9xNbT72QZqFxzLlo0vzWldijnaPcqukUhTaeIYe1AObI9v3ySAa72GkGCHaSkQqvBLydCJt2mu3zJYhPMKre1oNmQkGYUxLKCwonbABaugOEl7t1vL8mAMlwFg2ihJbYiogGfr2Imt/Y1jy8rftiW41opX1UQ30rgfRYeuEsKvVwuoyqffGHeBSjs53xZkYStYKj0m+8AQIDAQAB
-----END PUBLIC KEY-----

1
alipay/requirements.txt Normal file
View File

@ -0,0 +1 @@
qrcode

1
alipay/test/appid.txt Normal file
View File

@ -0,0 +1 @@
2021004117685170

1
alipay/test/huidiao.txt Normal file
View File

@ -0,0 +1 @@
https://www.kaiyuancloud.cn/test/pay/payhuidiao.dspy

3
alipay/test/private.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2eSZvVqn+PKRijVTUt0t6WP5O0JM/F2buhF2sfnQjZf7cnU13GCwIZLOjtko+h484rBitO7d1yCb/90FHnB0nvqsYFSUbLrViU4Zz9iljLqIvo4O+NFli+esbnC6MLs1jkCnfHhNLaXyNBfY+2DX4KYjJ376xBVWtwkb5a+jTWitQzufZwi2ihUgY87jLlxw1eb3R5V0g3cP5fF9zrbNHycPfw+qFu5WcOz22Pq2/WTECY7iX+9boXuzpwXexTm2MK/lhqNJGufWHtdSSKfurso0leMXBwmWkoYQ3s2SyFFFropbDtqio0zbu2QV52Dhmt06tDKYBdTJVcWKRlpqvAgMBAAECggEADyvphy5Uef/+xvbrS7hlvSlzE4iCBafjdgdpZnMpItxTIXKuwy0uVBBTY1Q/vg01Wkfs0VJ6wX8/xt5+/f476lcPELSJXkY27JL3ReL8s/NnpsBYrtLZb8JJywTkVeF6UEHnSVbN0eJWab9KJxWA/z4s0mQZDczcv9K8ZfaT6CadyV5iKmlfChHtU8cVzL/Shnkeg0ynOwNlbRQ7tyT6MCok1Rm/B5BIeRV9wLxdtr4ff14X9rwP+S8UVNNyiEKTX8QB1OT5GsNwmRkWX3Ud/qrb03BKuijbU5ms3K8+XV9SZWR/nZAieJ10zUFx66p1W6hjRQg31VN3v/1LvuwYiQKBgQD6SB4dT3+0tAfUH6O3JwtVgxP3PV7oqH0sWFzy+jXAkg16H6o3rG/FVO09n3sHu4UCx2IO0YzYbRY/x4AgJySUkQx3i1gvEjtMBm+GTuTv3DRKolJIPBFzl+fd6D5KMNm08yQBQgb2O6Nd1pbG8upkEt1BsyJXKe9XyWg6+vyKpQKBgQC6pGzIz7gvksjWyfux+HjUcs1GlYEG+RN5xijsbumIYpA4TxVqz+6l+nH+21Pb/bqckOg7GIIhDE/jHpnibfhAsv062nVgdRF2CRCVjfJWkUwPkiG8yNjDPC0Pe/GN3IcngExcX+ssZrT5DjF2thSKa7rn12iBajsOZIXCzbfTwwKBgD6WrFk+GTySTObiJqnVrMLsraCFi2d5QxxE2LG2mpyWqaIhqwqTJ7xcWZuwdy7e2Qtx2vbDtook9YxrkoH35/DzOP/oK9xRndyMO3WF40CMe9MyNotz4hVbJpPa0UDyaG8U4qsh3OXU0izUTg5gjvcJtKUKbAsKJJ16c4NUYrU1AoGAXvc+qsaWTw5+xlsriBiHUoG/VQTJDAU8FZ/wPq0Igm60NxJ2MUzvfuB5lFWAy9TEWNmacEc+Hxamp0dwwTd3M2RfWMzDIHswkUB3gcEh504yx1FxfR8su9ooi1JjS+1Dj6PGNtJPVN5Fgtvn6yEmPnAmP8To1cB5oA8hyEboHAUCgYEAyTnK5p3pexDtuBD1K5b2dv3oPgZ4QfbPt9vQ2RIYFGJ3GM9T0c5DlVzwZiYHNktNoK4NyogRh2wDK/2M2CZl3XrYUhqQCI4jtr16o77lYl8PA5fsfxNI/pmK/9vJ8UDFxzdvat2nkU18AxMmWQHSM6AMfrrDylDtbUjUdwIJSj8=
-----END RSA PRIVATE KEY-----

3
alipay/test/public.txt Normal file
View File

@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgD4YlfnWKd4vEHsim6vxLwustbnBBh9IUJwF5rGJ3b7wjYyzMnQZ36Cgf81A685IQ+Ni9GogNDaUWZx9V+qGxZRwaLbktSLnUNwPMudKlUoPyQtqyygU+Bmwg1B+UBzZsz8eG72qOuvu9xNbT72QZqFxzLlo0vzWldijnaPcqukUhTaeIYe1AObI9v3ySAa72GkGCHaSkQqvBLydCJt2mu3zJYhPMKre1oNmQkGYUxLKCwonbABaugOEl7t1vL8mAMlwFg2ihJbYiogGfr2Imt/Y1jy8rftiW41opX1UQ30rgfRYeuEsKvVwuoyqffGHeBSjs53xZkYStYKj0m+8AQIDAQAB
-----END PUBLIC KEY-----

56
b/Endofday/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,32 @@
async def supplier_accounts(ns):
"""供应商日结"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
account_config = await sor.R('account_config',{'partytype':'供应商','del_flg':'0'})
provider = await sor.R('provider',{"del_flg":'0','settle_mode':'1'})
for i in provider:
org = await sor.R('organization', {"id": i['orgid'],'del_flg':'0'})
for j in account_config:
acc = await getAccountByName(sor, org[0]['parentid'], i['orgid'], j['subjectname'])
if acc != None:
accountid = await sor.R('acc_balance',{'accountid':acc})
if len(accountid) >= 1:
settle_log = {
'accounting_orgid': org[0]['parentid'],
'providerid': i['orgid'],
'settle_date': await get_business_date(sor=None),
'settle_mode': '1',
'sale_mode': '',
'settle_amt': accountid[0]['balance'],
'business_op': 'SETTLE'
}
ai = SettleAccounting(settle_log)
await ai.accounting(sor)
return {'status': True, 'msg': '成功'}
except Exception as e:
raise e
return {'status': False, 'msg': '失败'}
ret = await supplier_accounts(params_kw)
return ret

View File

@ -0,0 +1,25 @@
async def addOrganization(ns):
"""
添加机构
:param orgname:
:param contactor:
:param contact_method:
:param reseller_id:
:param province_id:
:param city_id:
:param distinct_id:
:param address:
:param main_business:
:param orgcode:
:param license_img:
:param `org_type` VARCHAR(1) comment '机构类型1为机构2为个人客户、3为公司客户':
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
ns['id'] = uuid()
await sor.C('organization', ns)
return {'status': True, 'msg': '添加成功'}
ret = await addOrganization(params_kw)
return ret

View File

@ -0,0 +1,38 @@
async def delOrganization(ns,sor=None):
"""删除机构"""
if not sor:
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['del_flg'] = '1'
await sor.U('organization', ns)
sql_user = """update users set del_flg = 1 where orgid = '%s'""" % ns.get('id')
await sor.sqlExe(sql_user,{})
sql_customer = """update customer set del_flg = 1 where customerid = '%s'""" % ns.get('id')
await sor.sqlExe(sql_customer,{})
dictns = {'parentid': ns['id']}
reacs = await sor.R('organization', dictns)
for i in reacs:
await delOrganization({'id': i['id']},sor)
return {'status': True, 'msg': '删除成功'}
else:
if ns:
ns['del_flg'] = '1'
await sor.U('organization', ns)
sql_user = """update users set del_flg = 1 where orgid = '%s'""" % ns.get('id')
await sor.sqlExe(sql_user, {})
sql_customer = """update customer set del_flg = 1 where customerid = '%s'""" % ns.get('id')
await sor.sqlExe(sql_customer, {})
dictns = {'parentid': ns['id']}
reacs = await sor.R('organization', dictns)
for i in reacs:
await delOrganization({'id': i['id']}, sor)
return {'status': True, 'msg': '删除成功'}
return {'status': False, 'msg': '删除失败'}
ret = await delOrganization(params_kw)
return ret

View File

@ -0,0 +1,55 @@
async def getOrganization(ns):
"""展示机构"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not ns.get('parentid') and not ns.get('type'):
sql = """select * from organization where org_type = ${org_type}$ and del_flg = 0"""
reacs = await sor.sqlExe(sql,ns)
list = []
dict = {}
if reacs:
for i in reacs:
dict[i['id']] = i
for j in reacs:
if j['parentid'] == None or j['parentid'] == '':
list.append(j)
else:
if 'son' not in dict[j['parentid']]:
dict[j['parentid']]['son'] = []
dict[j['parentid']]['son'].append(j)
return {'status': True, 'data': list}
elif ns.get('parentid'):
sql = """select * from organization where org_type = ${org_type}$ and del_flg = 0 and parentid = ${parentid}$ """
reacs = await sor.sqlExe(sql, ns)
return {'status': True, 'data': reacs}
elif ns.get('type'):
user = await sor.R('users', {'del_flg': '0', 'id': ns['id']})
orgid = user[0]['orgid']
if ns.get('orgname'):
datalist = []
reacs = await sor.R('customer', {'del_flg': '0'})
for i in reacs:
userid = await sor.R('users', {'del_flg': '0', 'id': i['salemanid']})
if len(userid) >= 1:
if userid[0]['orgid'] == orgid:
nss = {'id': i['customerid'], 'pattern': '%' + ns.get('orgname') + '%'}
sql = """select * from organization where del_flg = 0 and orgname like ${pattern}$ and id = ${id}$"""
reacse = await sor.sqlExe(sql, nss)
if len(reacse) >= 1:
datalist.append(reacse[0])
return {'status': True, 'data': datalist}
else:
datalist = []
reacs = await sor.R('customer', {'del_flg': '0'})
for i in reacs:
userid = await sor.R('users', {'del_flg': '0','id':i['salemanid']})
if len(userid) >= 1:
if userid[0]['orgid'] == orgid:
organization = await sor.R('organization', {'del_flg': '0','id':i['customerid']})
if len(organization) >= 1:
if organization[0] not in datalist:
datalist.append(organization[0])
return {'status': True, 'data': datalist}
ret = await getOrganization(params_kw)
return ret

56
b/Organization/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,22 @@
async def myOrganization(ns):
"""我的机构
参数:
id:当前登录者id
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['sort'] = 'create_at desc'
ns['del_flg'] = '0'
user = await sor.R('users', ns)
ns['id'] = user[0]['orgid']
reacs = await sor.R('organization', ns)
nss = {'orgid': user[0]['orgid'], 'del_flg': '0'}
userall = await sor.R('users', nss)
if ns.get('uid'):
userall = await sor.R('users', {'id':ns.get('uid')})
return {'status': True, 'dada': reacs, 'users': userall}
return {'status': False, 'msg': '参数错误'}
ret = await myOrganization(params_kw)
return ret

View File

@ -0,0 +1,12 @@
async def upOrganization(ns):
"""修改机构"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
await sor.U('organization', ns)
return {'status': True, 'msg': '修改成功'}
return {'status': False, 'msg': '修改失败'}
ret = await upOrganization(params_kw)
return ret

45
b/README.md Normal file
View File

@ -0,0 +1,45 @@
# gadget
a light wight web server base on aiohttp
## Dependent
* [ahserver](https://github.com/yumoqing/ahserver)
* [sqlor](https://github.com/yumoqing/sqlor) if you want to use database
* [apppublic](https://github.com/yumoqing/apppublic)
## Download
```
git clone git@github.com:yumoqing/gadget.git
```
## Configuration
please look [ahserver](https://github.com/yumoqing/ahserver) to learn how to configure
### support https
under "website" in the conf/config.json file, identify ssl with "crtfile" and "keyfile"
like this.
```
"website":{
"ssl":{
"crtfile":"$[workdir]$/conf/www.bsppo.com.pem",
"keyfile":"$[workdir]$/conf/www.bsppo.com.key"
}
}
```
### log configure
In the conf/config.json, need to config log, you need to identify "name", "levelname" and "logfile"
```
"logger":{
"name":"gadget",
"levelname":"debug",
"logfile":"$[workdir]$/logs/gadget.log"
}
```
## Test
the test folder contains everything need for a base test.
please to go test folder and run
```
python ../src/main.py
```

0
b/__init__.py Normal file
View File

31
b/account/addledgers.dspy Normal file
View File

@ -0,0 +1,31 @@
async def addledgers(ns):
""" 录入客户余额
action: RECHARGE = 充值RECHARGE_REVERSE = 充值冲账
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not ns.get('apv_id'):
return {'status': False, 'msg': '充值失败'}
if ns.get('apv_id'):
if ns.get('status') != 'agree':
return {'status': False, 'msg': '充值失败'}
apv = await sor.R('apv_data',{'apv_id':ns.get('apv_id')})
balance = float(apv[0]['apv_text'][5:])
date = await get_business_date(sor=None)
recharge_log = {'customerid': apv[0]['result_org'], 'recharge_amt': balance,
'action': 'RECHARGE', 'recharge_path': '2', 'recharge_date': date}
try:
ra = RechargeAccounting(recharge_log)
await ra.accounting(sor)
await sor.C('recharge_log', {'id': uuid(), 'customerid': apv[0]['result_org'],
'recharge_date': datetime.datetime.now().strftime("%Y-%m-%d"),
'recharge_path': '2', 'recharge_amt': balance,
'recharge_timestamp': date, 'action': 'RECHARGE', 'op_userid': apv[0]['apv_sender'],
})
return {'status': True, 'msg': '充值成功'}
except Exception as e:
raise e
return {'status': False, 'msg': '充值失败'}
ret = await addledgers(params_kw)
return ret

122
b/account/email_info.dspy Normal file
View File

@ -0,0 +1,122 @@
# 输入邮件地址,口令和 POP3 服务器地址
email = "billing-specific@kaiyuancloud.cn"
password = "KYY@1234"
# 在对于的邮箱设置的SMTP/POP3里找到对应的服务地址
pop3_server = "pop.qiye.aliyun.com"
# 连接到POP3服务器:
server = poplib.POP3(pop3_server)
# 可以打开或关闭调试信息:
server.set_debuglevel(1)
# 可选:打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8'))
# 身份认证
server.user(email)
server.pass_(password)
# stat()返回邮件数量和占用空间:
print('邮件数量: %s. 大小: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 获取最新一封邮件, 注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index)
# lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍后解析出邮件,即完成下载邮件
msg = Parser().parsestr(msg_content)
# 接下来解析文件
async def decode_str(s):
value, charset = decode_header(s)[0]
if charset:
value = value.decode(charset)
return value
async def guess_charset(msg):
charset = msg.get_charset()
if charset is None:
content_type = msg.get('Content-Type', '').lower()
pos = content_type.find('charset=')
if pos >= 0:
charset = content_type[pos + 8:].strip()
return charset
# indent用于缩进显示:
async def email_info(msg, indent=0):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if indent == 0:
for header in ['From', 'To', 'Subject']:
value = msg.get(header, '')
if value:
if header=='Subject':
value = await decode_str(value)
else:
hdr, addr = parseaddr(value)
name = await decode_str(hdr)
value = u'%s <%s>' % (name, addr)
print('%s%s: %s' % (' ' * indent, header, value))
if (msg.is_multipart()):
parts = msg.get_payload()
for n, part in enumerate(parts):
print('%spart %s' % (' ' * indent, n))
print('%s--------------------' % (' ' * indent))
await email_info(part, indent + 1)
else:
content_type = msg.get_content_type()
if content_type=='text/plain' or content_type=='text/html':
content = msg.get_payload(decode=True)
charset = await guess_charset(msg)
if charset:
content = content.decode(charset)
try:
#解析交易金额
start_index = content.find("交易金额:") + len("交易金额:")
end_index = content.find("\n", start_index)
find_data = content[start_index:end_index]
price = find_data.strip()[1:]
#解析交易id码
start_index = content.find("摘要:") + len("摘要:")
end_index = content.find("\n", start_index)
find_data = content[start_index:end_index]
index = find_data.index("<")
name = find_data[:index]
if price and name:
mail_code_sql = """SELECT * FROM mail_code WHERE LOCATE(mailcode, '%s') > 0 and del_flg = '0';""" % name
mail_code = await sor.sqlExe(mail_code_sql, {})
# mail_code = await sor.R('mail_code',{'mailcode':name,'del_flg':'0'})
date = await get_business_date(sor=None)
recharge_log = {'customerid': mail_code[0]['customer_id'], 'recharge_amt': price,
'action': 'RECHARGE', 'recharge_path': '2', 'recharge_date': date}
try:
ra = RechargeAccounting(recharge_log)
await ra.accounting(sor)
await sor.C('recharge_log', {'id': uuid(), 'customerid': mail_code[0]['customer_id'],
'recharge_date': datetime.datetime.now().strftime("%Y-%m-%d"),
'recharge_path': '2', 'recharge_amt': price,
'recharge_timestamp': date, 'action': 'RECHARGE',
# 'op_userid': apv[0]['apv_sender'],
})
await sor.U('mail_code', {'id': mail_code[0]['id'], 'del_flg': '1'})
return {'status': True, 'msg': '充值成功'}
except Exception as e:
raise e
except:
pass
else:
print('%sAttachment: %s' % (' ' * indent, content_type))
return {"code":'200'}
msg = Parser().parsestr(msg_content)
ret = await email_info(msg)
return ret

View File

@ -0,0 +1,35 @@
async def getrechargelog(ns):
""" 展示我的客户充值记录 """
db = DBPools()
users_id = await get_user()
async with db.sqlorContext('kboss') as sor:
recharge_log = await sor.R('recharge_log',
{'sort': 'create_at desc', 'page': ns['page'], 'op_userid': users_id,
'del_flg': '0'})
if len(recharge_log['rows']) >= 1:
billid = await sor.R('bill', {'sort': 'create_at desc','customerid': recharge_log['rows'][0]['customerid']})
for j in recharge_log['rows']:
RECHARGE_REVERSE = await sor.R('recharge_log',
{'sort': 'create_at desc','del_flg': '0', 'op_userid': users_id, 'original_id': j['id']})
if len(RECHARGE_REVERSE) >= 1:
j['RECHARGE_REVERSE'] = '冲账'
org = await sor.R('organization', {'sort': 'create_at desc','id': j['customerid'], 'del_flg': '0'})
if len(org) >= 1:
j['customerid'] = org[0]['orgname']
j['orgid'] = org[0]['id']
if j['recharge_path'] == '0':
j['recharge_path'] = '支付宝'
elif j['recharge_path'] == '1':
j['recharge_path'] = '微信'
elif j['recharge_path'] == '2':
j['recharge_path'] = '线下充值'
if j['action'] == 'RECHARGE':
j['action'] = '充值'
else:
j['action'] = '充值冲账'
else:
continue
return {'status': True, 'data': recharge_log,'billid':billid}
ret = await getrechargelog(params_kw)
return ret

56
b/account/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,9 @@
async def openacc(ns):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
accounting_orgid = ns.get('accounting_orgid') #'sBAkKZjSl35T5lr3p9-_1'
orgid = ns.get('orgid') #'wuNnN1VtiwipC_ju1tKOW'
await openProviderAccounts(sor, accounting_orgid, orgid)
return {'code':200}
ret = await openacc(params_kw)
return ret

7
b/account/openacc.dspy Normal file
View File

@ -0,0 +1,7 @@
async def openacc(ns):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await openOwnerAccounts(sor,'mIWUHBeeDM8mwAFPIQ8pS')
return {'code':200}
ret = await openacc(params_kw)
return ret

View File

@ -0,0 +1,64 @@
async def settle_accounts(ns):
"""供应商结算统计 年、月、日、周、季
0实时结算1:日结2:周结3:月结4:季结5年结
参数id 用户id
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
orgid = await sor.R('users',ns)
account = await sor.R('account', {'id':orgid[0]['orgid']})
if len(account) >= 1:
newdate = await get_business_date(sor=None)
provider = await sor.R('provider',{'orgid':orgid[0]['orgid']})
if provider[0]['settle_mode'] == '1':
date = strdate_add(newdate,-1,0,0)
bill = await sor.R('bill',{'providerid':provider[0]['id'],'bill_date':date})
if len(bill) >= 1:
for i in bill:
org = await sor.R('organization',{'id':i['customerid']})
i['customerid'] = org[0]
elif provider[0]['settle_mode'] == '2':
date = strdate_add(newdate, -7, 0, 0)
nss = {'providerid': provider[0]['id'], 'bill_date': newdate, 'date': date}
sql = """select * from bill where bill_date > ${date}$ and bill_date < ${newdate}$ and providerid = ${providerid}$;"""
bill = await sor.sqlExe(sql,nss)
if len(bill) >= 1:
for i in bill:
org = await sor.R('organization', {'id': i['customerid']})
i['customerid'] = org[0]
elif provider[0]['settle_mode'] == '3':
date = strdate_add(newdate, 0, -1, 0)
nss = {'providerid': provider[0]['id'], 'bill_date': newdate, 'date': date}
sql = """select * from bill where bill_date > ${date}$ and bill_date < ${newdate}$ and providerid = ${providerid}$;"""
bill = await sor.sqlExe(sql, nss)
if len(bill) >= 1:
for i in bill:
org = await sor.R('organization', {'id': i['customerid']})
i['customerid'] = org[0]
elif provider[0]['settle_mode'] == '4':
date = strdate_add(newdate, 0, -3, 0)
nss = {'providerid': provider[0]['id'], 'bill_date': newdate, 'date': date}
sql = """select * from bill where bill_date > ${date}$ and bill_date < ${newdate}$ and providerid = ${providerid}$;"""
bill = await sor.sqlExe(sql, nss)
if len(bill) >= 1:
for i in bill:
org = await sor.R('organization', {'id': i['customerid']})
i['customerid'] = org[0]
elif provider[0]['settle_mode'] == '5':
date = strdate_add(newdate, 0, 0, -1)
nss = {'providerid': provider[0]['id'], 'bill_date': newdate, 'date': date}
sql = """select * from bill where bill_date > ${date}$ and bill_date < ${newdate}$ and providerid = ${providerid}$;"""
bill = await sor.sqlExe(sql, nss)
if len(bill) >= 1:
for i in bill:
org = await sor.R('organization', {'id': i['customerid']})
i['customerid'] = org[0]
return {'status': True, 'data': bill}
except Exception as e:
raise e
return {'status': False, 'msg': '获取失败'}
ret = await settle_accounts(params_kw)
return ret

30
b/account/upledger.dspy Normal file
View File

@ -0,0 +1,30 @@
async def upledger(ns):
""" 客户充值冲账
action: RECHARGE = 充值RECHARGE_REVERSE = 充值冲账
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not ns.get('apv_id'):
return {'status': False, 'msg': '充值失败'}
if ns.get('apv_id'):
if ns.get('status') != 'agree':
return {'status': False, 'msg': '充值失败'}
apv = await sor.R('apv_data',{'apv_id':ns.get('apv_id')})
balance = float(apv[0]['apv_text'][5:])
date = await get_business_date(sor=None)
recharge_log = {'customerid': apv[0]['result_org'], 'recharge_amt': balance,
'action': 'RECHARGE_REVERSE', 'recharge_path': '2', 'recharge_date': date}
ra = RechargeAccounting(recharge_log)
r = await ra.accounting(sor)
if r == True:
await sor.C('recharge_log', {'id': uuid(), 'customerid': apv[0]['result_org'],'recharge_date': date,
'recharge_path': '2', 'recharge_amt': balance,
'recharge_timestamp': datetime.datetime.now(), 'action': 'RECHARGE_REVERSE',
'original_id': apv[0]['original_id'],
'op_userid': apv[0]['apv_sender']})
return {'status': True, 'msg': '充值冲账成功'}
else:
return {'status': True, 'msg': '充值冲账失败'}
ret = await upledger(params_kw)
return ret

30
b/account/upledgers.dspy Normal file
View File

@ -0,0 +1,30 @@
async def upledgers(ns):
""" 客户充值冲账
action: RECHARGE = 充值RECHARGE_REVERSE = 充值冲账
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not ns.get('apv_id'):
return {'status': False, 'msg': '充值失败'}
if ns.get('apv_id'):
if ns.get('status') != 'agree':
return {'status': False, 'msg': '充值失败'}
apv = await sor.R('apv_data',{'apv_id':ns.get('apv_id')})
balance = float(apv[0]['apv_text'][7:])
date = await get_business_date(sor=None)
recharge_log = {'customerid': apv[0]['result_org'], 'recharge_amt': balance,
'action': 'RECHARGE_REVERSE', 'recharge_path': '2', 'recharge_date': date}
ra = RechargeAccounting(recharge_log)
r = await ra.accounting(sor)
if r == True:
await sor.C('recharge_log', {'id': uuid(), 'customerid': apv[0]['result_org'],'recharge_date': date,
'recharge_path': '2', 'recharge_amt': balance,
'recharge_timestamp': datetime.datetime.now(), 'action': 'RECHARGE_REVERSE',
'original_id': apv[0]['original_id'],
'op_userid': apv[0]['apv_sender']})
return {'status': True, 'msg': '充值冲账成功'}
else:
return {'status': True, 'msg': '充值冲账失败'}
ret = await upledgers(params_kw)
return ret

View File

@ -0,0 +1,54 @@
async def appCodesAdd(ns={}):
"""
add new app code
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
:param ns:
:return:
"""
db = DBPools()
# if add kv table and id exists
if ns.get('kv'):
ns['id'] = uuid()
async with db.sqlorContext('kboss') as sor:
try:
await sor.C('appcodes_kv', ns)
return {
"status": True,
"msg": "appcodes_kv add success"
}
except Exception as e:
return {
"status": False,
"msg": "appcodes_kv add failed"
}
app_code = {
'id': ns.get('id'),
'name': ns.get('name'),
'hierarchy_flg': ns.get('hierarchy_flg')
}
async with db.sqlorContext('kboss') as sor:
try:
if not ns.get('id'):
return {
"status": False,
"msg": "app code id is empty"
}
# insert into appcodes
await sor.C('appcodes', app_code)
return {
"status": True,
"msg": "app codes add success"
}
except Exception as e:
return {
"status": False,
"msg": "app codes add failed"
}
ret = await appCodesAdd(params_kw)
return ret

View File

@ -0,0 +1,48 @@
async def appCodesDelete(ns={}):
"""
delete app code
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
:param ns:
:return:
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if not ns.get('id'):
return {
"status": False,
"msg": "appCodes id is empty, please check"
}
if ns.get('kv'):
ns['del_flg'] = '1'
try:
await sor.U('appcodes_kv', ns)
return {
"status": True,
"msg": "appCodes_kv delete success"
}
except Exception as e:
return {
"status": False,
"msg": "appCodes_kv delete failed"
}
try:
ns['del_flg'] = '1'
await sor.U('appcodes',ns)
delete_kv_sql = """update appcodes_kv set del_flg = 1 where codeid = '%s'""" % ns.get('id')
await sor.sqlExe(delete_kv_sql, {})
return {
"status": True,
"msg": "appCodes delete success"
}
except Exception as e:
return {
"status": False,
"msg": "appCodes delete failed"
}
ret = await appCodesDelete(params_kw)
return ret

View File

@ -0,0 +1,43 @@
async def appCodesSearch(ns={}):
"""
search new appcodes
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
:param ns:
:return:
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
ns['del_flg'] = '0'
ns['sort'] = 'create_at'
ns['order'] = 'desc'
ns['page'] = ns.get('page') if ns.get('page') else 1
if ns.get('kv'):
if not ns.get('codeid'):
return {
"status": False,
"msg": "appCodes_kv search failed, the id is empty",
"data": ""
}
app_code_result = await sor.R('appcodes_kv', ns)
else:
app_code_result = await sor.R('appcodes',ns)
return {
"status": True,
"msg": "appCodes search success",
"data": app_code_result
}
except Exception as e:
return {
"status": False,
"msg": "appCodes search failed",
"data": ""
}
ret = await appCodesKvSearch(params_kw)
return ret

View File

@ -0,0 +1,66 @@
async def get_children(parent_id, data):
children = []
for item in data:
if item['parentid'] == parent_id:
child = {'id': item['id'], 'codeid': item['codeid'], 'parentid': item['parentid'], 'k': item['k'], 'v': item['v']}
grandchildren = await get_children(item['id'], data)
if grandchildren:
child['son'] = grandchildren
children.append(child)
return children
async def transform_data(data):
result = []
for item in data:
if item['parentid'] is None or item['parentid'] == '':
parent = {'id': item['id'], 'codeid': item['codeid'], 'parentid': item['parentid'], 'k': item['k'], 'v': item['v']}
children = await get_children(item['id'], data)
if children:
parent['son'] = children
result.append(parent)
return result
async def appCodesSearch(ns={}):
"""
search new appcodes
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
:param ns:
:return:
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
ns['del_flg'] = '0'
ns['sort'] = 'create_at'
ns['order'] = 'desc'
ns['page'] = ns.get('page') if ns.get('page') else 1
if ns.get('kv'):
if not ns.get('codeid'):
return {
"status": False,
"msg": "appCodes_kv search failed, the codeid is empty",
"data": ""
}
app_code_result = await sor.R('appcodes_kv', ns)
app_code_result = await transform_data(app_code_result.get('rows'))
else:
app_code_result = await sor.R('appcodes',ns)
return {
"status": True,
"msg": "appCodes search success",
"data": app_code_result
}
except Exception as e:
return {
"status": False,
"msg": "appCodes search failed",
"data": ""
}
ret = await appCodesSearch(params_kw)
return ret

View File

@ -0,0 +1,46 @@
async def appCodesUpdate(ns={}):
"""
delete app code
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
:param ns:
:return:
"""
ns_appcode = {
'id': ns.get('id'),
'name': ns.get('name'),
'hierarchy_flg': ns.get('hierarchy_flg')
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
if ns.get('kv'):
try:
await sor.U('appcodes_kv', ns)
return {
"status": True,
"msg": "appcodes_kv update success"
}
except Exception as e:
return {
"status": False,
"msg": "appcodes_kv update failed",
}
await sor.U('appcodes',ns_appcode)
return {
"status": True,
"msg": "appcodes update success",
}
except Exception as e:
return {
"status": False,
"msg": "appcodes update failed",
}
ret = await appCodesUpdate(params_kw)
return ret

6
b/appcode/getTime.dspy Normal file
View File

@ -0,0 +1,6 @@
async def get_server_timestamp(params_kw=None):
return str(int(time.time() * 1000))
ret = await get_server_timestamp(params_kw=1)
return ret

56
b/appcode/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,291 @@
### 编码增加
请求URL
<http://47.93.12.75:8888/appcode/appCodesAdd.dspy>
请求示例
```
添加Code表
{
'id': 'user_status',
'name': '用户状态',
'hierarchy_flg': '0'
}
添加kv表
{
'kv': '1',
'codeid':'user_status',
'parentid': '123',
'k': '2',
'v': '休息中'
}
```
请求参数说明
添加Code表
| 参数 | 类型 | 必填 | 描述 |
| ------------- | ------ | ---- | --------------------- |
| id | String | 是 | 英文名称例如status |
| name | String | 是 | 中文名称 例如:状态 |
| hierarchy_flg | String | 是 | 多级标志 0/1 是否多级 |
添加kv表
| 参数 | 类型 | 必填 | 描述 |
| -------- | ------ | ---- | -------------- |
| kv | String | 是 | 添加kv表 值为1 |
| codeid | String | 是 | Code表中的id |
| parentid | String | 是 | 父级id |
| k | String | 是 | 键 |
| v | String | 是 | 值 |
返回示例
```
添加成功
{
"status": true,
"msg": "add success"
}
添加失败
{
"status": False,
"msg": "add failed"
}
```
### 编码删除
请求URL
<http://47.93.12.75:8888/appcode/appCodesDelete.dspy>
请求示例
```
删除Code表对应字段
{
'id': 'user_status'
}
添加kv表
{
'kv': '1',
'id': 'kv表中对应id'
}
```
请求参数说明
| 参数 | 类型 | 必填 | 描述 |
| ---- | ------ | ---- | --------------- |
| kv | String | 是 | 删除kv表 值为1 |
| id | String | 是 | Code/kv表中的id |
返回示例
```
删除成功
{
"status": true,
"msg": "appCodes delete success"
}
删除失败
{
"status": False,
"msg": "appCodes delete failed"
}
```
### 编码更新
请求URL
<http://47.93.12.75:8888/appcode/appCodesUpdate.dspy>
请求示例
```
更新Code表
{
'id': 'user_status',
'name': '用户状态',
'hierarchy_flg': '0'
}
更新kv表
{
'kv': '1',
'codeid':'user_status',
'parentid': '123',
'k': '2',
'v': '休息中'
}
```
请求参数说明
更新Code表
| 参数 | 类型 | 必填 | 描述 |
| ------------- | ------ | ---- | --------------------- |
| id | String | 是 | 英文名称例如status |
| name | String | 否 | 中文名称 例如:状态 |
| hierarchy_flg | String | 否 | 多级标志 0/1 是否多级 |
更新kv表
| 参数 | 类型 | 必填 | 描述 |
| -------- | ------ | ---- | -------------- |
| kv | String | 是 | 更新kv表 值为1 |
| codeid | String | 否 | Code表中的id |
| parentid | String | 否 | 父级id |
| k | String | 否 | 键 |
| v | String | 否 | 值 |
返回示例
```
添加成功
{
"status": true,
"msg": "update success"
}
添加失败
{
"status": False,
"msg": "update failed"
}
```
### 编码查询
请求URL
<http://47.93.12.75:8888/appcode/appCodesSearch.dspy>
请求示例
```
查询code表时不需要参数
返回data为[{},{},{}]
查询kv表
{
'kv': '1',
'codeid': 'kv表中对应id'
}
```
请求参数说明
| 参数 | 类型 | 必填 | 描述 |
| ---- | ------ | ----------------- | --------------- |
| kv | String | 否/查询kv为必填项 | 查询kv表 值为1 |
| id | String | 否/查询kv为必填项 | Code/kv表中的id |
返回示例
```
查询成功
{
"status": True,
"msg": "appCodes_kv search success",
"data": app_code_result
}
查询失败
{
"status": False,
"msg": "appCodes_kv search failed",
"data": ""
}
查询成功示例
[
{
"parentid": null,
"k": "0",
"v": "未认证",
"id": "8KxM0iIY",
"codeid": "status"
},
{
"parentid": null,
"k": "1",
"v": "认证",
"id": "GZx0dzSd",
"codeid": "status",
"son": [
{
"parentid": "1",
"k": "0",
"v": "手机号",
"id": "37HmJ8T7",
"codeid": "status",
"son": [
{
"parentid": "1-0",
"k": "0",
"v": "华南地区",
"id": "eVF9aerQ",
"codeid": "status"
},
{
"parentid": "1-0",
"k": "1",
"v": "华中地区",
"id": "j1rhnW6p",
"codeid": "status"
},
{
"parentid": "1-0",
"k": "2",
"v": "华北地区",
"id": "Cr0nWLIU",
"codeid": "status",
"son": [
{
"parentid": "1-0-2",
"k": "0",
"v": "甘肃",
"id": "Gnd5WA4b",
"codeid": "status"
},
{
"parentid": "1-0-2",
"k": "1",
"v": "内蒙古",
"id": "FRHOrEep",
"codeid": "status"
}
]
}
]
},
{
"parentid": "1",
"k": "1",
"v": "微信",
"id": "f1MRJscT",
"codeid": "status"
},
{
"parentid": "1",
"k": "2",
"v": "QQ",
"id": "m3M39BI8",
"codeid": "status"
}
]
}
]
```

View File

@ -0,0 +1,123 @@
编码增加
请求地址: http://47.93.12.75:8888/appcode/appCodesAdd.dspy
请求参数:
`id` VARCHAR(32) 'id',
`name` VARCHAR(255) '编码名称',
`hierarchy_flg` VARCHAR(1) '多级标志',
`del_flg` VARCHAR(1) DEFAULT '0' comment '删除标志',
`create_at` TIMESTAMP comment '创建时间戳'
从code表开始添加 同时添加code_kv表 需要提交的字段
{
'id': '英文字段 例如:user_status',
'name': '中文字段 例如: 用户状态',
'hierarchy_flg': '层级 默认是 0',
# 以下data是同时添加到kv表中的内容
'data':[
{'codeid':'user_status', 'parentid': '123', 'k': '0', 'v': '在线'},
{'codeid':'user_status', 'parentid': '123', 'k': '1', 'v': '未在线'}
]
}
单独添加code_kv表中字段需要提交的字段 kv和codeid为必填项
{'kv': '1', 'codeid':'user_status', 'parentid': '123', 'k': '1', 'v': '休息中'}
响应内容:
增加成功:
{
"status": True,
"msg": "app codes and appcodes_kv add success"
}
增加失败:
{
"status": False,
"msg": "app codes and appcodes_kv add failed"
}
编码删除
请求地址: http://47.93.12.75:8888/appcode/appCodesDelete.dspy
请求参数:
只删除kv表中对应字段对应参数 kv和id为必填项
{'kv': '1', 'id': 'kv表对应id'}
删除code表中字段(同时把kv表对应字段删除) (kv不能为0, 置空或不添加)
{'kv': '', 'id': 'code表中对应id'}
响应内容:
删除成功:
{
"status": True,
"msg": "appCodes delete success"
}
删除失败:
{
"status": False,
"msg": "appCodes delete failed"
}
编码更新
请求地址: http://47.93.12.75:8888/appcode/appCodesUpdate.dspy
请求参数:
只更新kv表中对应字段对应参数 kv和id为必填项
{'kv': '1', 'id': 'J9dAtdLP','codeid':'user_status', 'parentid': '123', 'k': '0', 'v': '在线'}
更新code表中字段(同时把kv表对应字段删除) id为必填项,(kv不能为0, 置空或不添加)
{
'kv': '',
'id': 'user_status',
'name': '客户状态',
'hierarchy_flg': '0'
}
响应内容:
更新成功:
{
"status": True,
"msg": "appcodes update success"
}
更新失败:
{
"status": False,
"msg": "appcodes update failed"
}
编码查询:
请求地址:
查询code:
http://47.93.12.75:8888/appcode/appCodesSearch.dspy
请求参数:
查询code:
查询code表时不需要参数
从code点击字段查询code_kv表: kv必填项 codeid为必填项
{
'kv': '1',
'codeid': 'user_status'
}
响应内容:
查询成功:
{
"status": True,
"msg": "appCodes_kv search success",
"data": app_code_result
}
查询失败:
{
"status": False,
"msg": "appCodes_kv search failed",
"data": ""
}

27
b/apv/apv_business.dspy Normal file
View File

@ -0,0 +1,27 @@
async def apv_business(ns={}):
try:
data = {
"id": ns.get("id", None),
"business_name": ns["business_name"],
"callback_url": ns["callback_url"],
"role_level": ns.get("role_level"),
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if data['id']:
data['update_at'] = '{}'.format(datetime.datetime.now())
num = await sor.U("apv_business", data)
if num == 0:
return {"status": False, "msg": "更新失败,数据未更改"}
else:
return {"status": True, "msg": "success"}
else:
data['id'] = uuid()
await sor.C("apv_business", data)
return {"status": True, "msg": "success", "data": data['id']}
ret = await apv_business(params_kw)
return ret

25
b/apv/apv_template.dspy Normal file
View File

@ -0,0 +1,25 @@
async def apv_template(ns={}):
try:
data = {
"id": ns.get("id", None),
"business_id": ns["business_id"],
"title_template": ns["title_template"],
"detail_template": ns["detail_template"],
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if data['id']:
data['update_at'] = '{}'.format(datetime.datetime.now())
num = await sor.U("apv_content_template", data)
if num == 0:
return {"status": False, "msg": "更新失败,数据未更改"}
else:
data['id'] = uuid()
await sor.C("apv_content_template", data)
return {"status": True, "msg": "success","data":data['id']}
ret = await apv_template(params_kw)
return ret

233
b/apv/callback_apv.dspy Normal file
View File

@ -0,0 +1,233 @@
class DingCallbackCrypto3:
def __init__(self, token, encodingAesKey, key):
self.encodingAesKey = encodingAesKey
self.key = key
self.token = token
self.aesKey = base64.b64decode(self.encodingAesKey + '=')
## 生成回调处理完成后的success加密数据
def getEncryptedMap(self, content):
encryptContent = self.encrypt(content)
timeStamp = str(int(datetime.datetime.now().timestamp()))
nonce = self.generateRandomKey(16)
sign = self.generateSignature(nonce, timeStamp, self.token, encryptContent)
return {'msg_signature': sign, 'encrypt': encryptContent, 'timeStamp': timeStamp, 'nonce': nonce}
##解密钉钉发送的数据
def getDecryptMsg(self, msg_signature, timeStamp, nonce, content):
"""
解密
:param content:
:return:
"""
sign = self.generateSignature(nonce, timeStamp, self.token, content)
print(sign, msg_signature)
if msg_signature != sign:
raise ValueError('signature check error')
# 对密文BASE64解码
content = base64.decodebytes(content.encode('UTF-8')) ##钉钉返回的消息体
iv = self.aesKey[:16] ##初始向量
aesDecode = AES.new(self.aesKey, AES.MODE_CBC, iv)
decodeRes = aesDecode.decrypt(content)
pad = int(decodeRes[-1])
if pad > 32:
raise ValueError('Input is not padded or padding is corrupt')
decodeRes = decodeRes[:-pad]
l = struct.unpack('!i', decodeRes[16:20])[0]
##获取去除初始向量四位msg长度以及尾部corpid
nl = len(decodeRes)
if decodeRes[(20 + l):].decode() != self.key:
raise ValueError('corpId 钉钉回调校验错误')
return decodeRes[20:(20 + l)].decode()
def encrypt(self, content):
"""
加密
:param content:
:return:
"""
msg_len = self.length(content)
content = ''.join([self.generateRandomKey(16), msg_len.decode(), content, self.key])
contentEncode = self.pks7encode(content)
iv = self.aesKey[:16]
aesEncode = AES.new(self.aesKey, AES.MODE_CBC, iv)
aesEncrypt = aesEncode.encrypt(contentEncode.encode('UTF-8'))
return base64.encodebytes(aesEncrypt).decode('UTF-8')
### 生成回调返回使用的签名值
def generateSignature(self, nonce, timestamp, token, msg_encrypt):
info(f"{type(nonce)=}, {type(timestamp)=}, {type(token)=}, {type(msg_encrypt)=}")
v = msg_encrypt
signList = ''.join(sorted([nonce, timestamp, token, v]))
return hashlib.sha1(signList.encode()).hexdigest()
def length(self, content):
"""
将msg_len转为符合要求的四位字节长度
:param content:
:return:
"""
l = len(content)
return struct.pack('>l', l)
def pks7encode(self, content):
"""
安装 PKCS#7 标准填充字符串
:param text: str
:return: str
"""
l = len(content)
output = io.StringIO()
val = 32 - (l % 32)
for _ in range(val):
output.write('%02x' % val)
# print "pks7encode",content,"pks7encode", val, "pks7encode", output.getvalue()
return content + binascii.unhexlify(output.getvalue()).decode()
def pks7decode(self, content):
nl = len(content)
val = int(binascii.hexlify(content[-1]), 16)
if val > 32:
raise ValueError('Input is not padded or padding is corrupt')
l = nl - val
return content[:l]
def generateRandomKey(self, size,
chars=string.ascii_letters + string.ascii_lowercase + string.ascii_uppercase + string.digits):
"""
生成加密所需要的随机字符串
:param size:
:param chars:
:return:
"""
return ''.join(random.choice(chars) for i in range(size))
async def save_data(apv_id, finis_hTime, apv_status) -> str:
"""
保存审批回调数据
:param apv_id: 审批id
:param finis_hTime: 创建时间
:param apv_status: 流程状态
"""
data = {
"apv_id": apv_id,
"apv_status": apv_status,
"update_at": str(datetime.datetime.now())[:19],
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
sql = "update apv_data set apv_status = ${apv_status}$ , apv_finish_time=${update_at}$,update_at=${update_at}$ where apv_id = ${apv_id}$"
await sor.sqlExe(sql, data)
data = {
"id": uuid(),
"apv_id": apv_id,
"apv_status": apv_status,
}
await sor.C("apv_status_history", data)
info("数据入库成功")
async def get_key(oid: str) -> str:
db = DBPools()
async with db.sqlorContext('kboss') as sor:
sql = "select http_token,http_aes_key,app_key from apv_key where del_flg = '0' and orgid = ${oid}$"
data = await sor.sqlExe(sql, {"oid": oid})
if data:
return data[0]
else:
raise f"oid:{oid},get key is null."
# 内部回调
async def call_back_inner(processInstanceId, status):
"""
内部回调
:param processInstanceId: apv_id
:param status: 审批状态
:return:
"""
# get callback url
db = DBPools()
async with db.sqlorContext('kboss') as sor:
sql = "SELECT b.callback_url FROM apv_data d LEFT JOIN apv_business b ON d.business_id=b.id WHERE d.apv_id=${processInstanceId}$"
data = await sor.sqlExe(sql, {"processInstanceId": processInstanceId})
if len(data) == 0:
info(f"钉钉内部回调获取失败apv_id:{processInstanceId}")
return 0
data = data[0]
url = data.get("callback_url") + f"?apv_id={processInstanceId}&status={status}"
# url = "https://dev.kaiyuancloud.cn/account/addledgers.dspy?apv_id=ehV03fARQjqtT69piTFtzw07441693812206&status=agree"
async with aiohttp_client.request("GET",url) as res:
try:
json_data = await res.json()
info(f"apv_id:{processInstanceId},回调内部响应:{res.status}resp json:{json_data}")
except Exception as e:
info(f"apv_id:{processInstanceId},回调内部响应:{res.status}resp:{res.text}")
async def get_sender_by_apv_id(apv_id):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_data", {"apv_id": apv_id})
if not data:
return None
else:
return data[0]["user_id"]
return None
async def callback_apv(ns={}):
"""
审批回调
:param ns:
:return:
"""
oid = ns.get("oid")
if not oid:
return "oid is None"
data = await get_key(oid)
token = password_decode(data["http_token"])
aes_key = password_decode(data["http_aes_key"])
key = password_decode(data["app_key"])
test = DingCallbackCrypto3(token, aes_key, key)
# 解密参数
text = test.getDecryptMsg(msg_signature=ns.get("signature"), timeStamp=ns.get("timestamp"), nonce=ns.get("nonce"), content=ns.get("encrypt"))
text = json.loads(text)
info(f"回调数据:{text}")
t = text.get("type", None)
if t != "finish":
status = t
else:
status = text.get("result", t)
processInstanceId = text.get("processInstanceId",None)
# 加密返回
res = test.getEncryptedMap("success")
if processInstanceId:
# 判断是否是 api 发起
f = await get_sender_by_apv_id(processInstanceId)
if not f:
info(f"apv_id:{processInstanceId} is not use api")
return res
# 保存数据
await save_data(apv_id=processInstanceId, finis_hTime=text.get("finishTime"), apv_status=status)
try:
# 回调内部接口
info(f"开始回调内部接口:{processInstanceId}")
await call_back_inner(processInstanceId=processInstanceId, status=status)
except Exception as e:
info(f"回调内部接口失败:{e}")
return res
ret = await callback_apv(params_kw)
return ret

View File

@ -0,0 +1,21 @@
async def delete_apv_business(ns={}):
try:
data = {
"id": ns["id"],
"del_flg": ns["del_flg"],
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data['update_at'] = '{}'.format(datetime.datetime.now())
sql = "update apv_business set del_flg = ${del_flg}$,update_at = ${update_at}$ where id = ${id}$ and del_flg = 0"
num = await sor.sqlExe(sql, data)
if num == 0:
return {"status": False, "msg": "删除失败,数据未更改"}
else:
return {"status": True, "msg": "success"}
ret = await delete_apv_business(params_kw)
return ret

View File

@ -0,0 +1,21 @@
async def delete_apv_template(ns={}):
try:
data = {
"id": ns["id"],
"del_flg": ns["del_flg"],
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data['update_at'] = '{}'.format(datetime.datetime.now())
sql = "update apv_content_template set del_flg = ${del_flg}$, update_at = ${update_at}$ where id = ${id}$ and del_flg = 0"
num = await sor.sqlExe(sql, data)
if num == 0:
return {"status": False, "msg": "删除失败,数据未更改"}
else:
return {"status": True, "msg": "success"}
ret = await delete_apv_template(params_kw)
return ret

View File

@ -0,0 +1,14 @@
async def get_apv_business(ns={}):
"""
获取业务列表
"""
ns["del_flg"] = ns.get("del_flg", "0")
ns["sort"] = ns.get("sort", "update_at desc")
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_business", ns)
return {"status": True, "data": data}
res = await get_apv_business(params_kw)
return res

36
b/apv/get_apv_flow.dspy Normal file
View File

@ -0,0 +1,36 @@
# 获取审批流参数配置
async def get_apv_flow(ns={}):
orgid = ns.get("orgid")
business_id = ns.get("business_id")
if not orgid or not business_id:
return {"status": False, "msg": "orgid or business_id is None"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_flow", {"orgid": orgid, "del_flg": 0, "business_id": business_id})
if not data:
return {"status": True, "data":{}}
flow = {}
flow["orgid"] = data[0]["orgid"]
flow["user_id"] = data[0]["user_id"]
flow["dd_template_id"] = data[0]["dd_template_id"]
flow["del_flg"] = data[0]["del_flg"]
flow["levels"] = {}
for i in data:
l_id = str(i["level"])
now_level = flow["levels"].get(l_id, None)
if now_level:
now_level["apv_dd_user_phone"][i["apv_dd_user_id_index"]] = i["apv_dd_user_phone"]
now_level["mode"] = i["mode"]
else:
user_data = {
"apv_dd_user_phone": {
i["apv_dd_user_id_index"]: i["apv_dd_user_phone"]
},
"mode": i["mode"]
}
flow["levels"][l_id] = user_data
data = flow
return {"status": True, "data": data}
ret = await get_apv_flow(params_kw)
return ret

11
b/apv/get_apv_form.dspy Normal file
View File

@ -0,0 +1,11 @@
# 获取审批表单
async def get_apv_form(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R('apv_form', {'del_flg': '0'})
return {"status": True, "data": data}
return {"status": False, "msg": "sql error"}
ret = await get_apv_form(params_kw)
return ret

21
b/apv/get_apv_key.dspy Normal file
View File

@ -0,0 +1,21 @@
# 获取审批参数配置
async def get_apv_key(ns={}):
orgid = ns.get('orgid')
if not orgid:
return {"status": False, "data": [], "msg": "orgid is null."}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R('apv_key', {'orgid': orgid, 'del_flg': '0'})
if not data:
return {"status": False, "data": [], "msg": "get key is null."}
data = data[0]
data["app_key"] = password_decode(data["app_key"])
data["app_secret"] = password_decode(data["app_secret"])
data["http_aes_key"] = password_decode(data["http_aes_key"])
data["http_token"] = password_decode(data["http_token"])
return {"status": True, "data": data}
return {"status": False, "data": [], "msg": "sql error"}
ret = await get_apv_key(params_kw)
return ret

14
b/apv/get_apv_list.dspy Normal file
View File

@ -0,0 +1,14 @@
# 获取审批列表
async def get_apv_list(ns={}):
user_id = ns.get("user_id")
if not user_id:
return {"status": False, "msg": "user_id is None"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_data", {"user_id": user_id, "del_flg": 0, "sort": "update_at desc"})
return {"status": True, "data": data}
ret = await get_apv_list(params_kw)
return ret

View File

@ -0,0 +1,17 @@
# 获取审批列表
async def get_apv_template(ns={}):
business_id = ns.get("business_id")
if not business_id:
return {"status": False, "msg": "business_id is None"}
ns["del_flg"] = ns.get("del_flg", "0")
ns["sort"] = ns.get("sort", "update_at desc")
db = DBPools()
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_content_template", ns)
return {"status": True, "data": data}
ret = await get_apv_template(params_kw)
return ret

56
b/apv/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

71
b/apv/save_apv_flow.dspy Normal file
View File

@ -0,0 +1,71 @@
async def save_apv_flow(ns={}):
flag = ns.get("flag", "").lower()
if flag not in ["add", "update"]:
return {"status": False, "msg": f"参数解析错误,请检查参数:flag"}
db = DBPools()
try:
async with db.sqlorContext('kboss') as sor:
try:
user_id = ns["user_id"]
orgid = ns["orgid"]
business_id = ns["business_id"]
level_data = ns["level_data"]
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
if not isinstance(level_data, list):
return {"status": False, "msg": f"参数类型错误,请检查参数:{level_data}"}
for _f, l in enumerate(level_data):
try:
level_mode = l["level_mode"]
apv_dd_user_phone = l["apv_dd_user_phone"]
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
if not isinstance(apv_dd_user_phone, list):
return {"status": False, "msg": f"参数类型错误,请检查参数:{apv_dd_user_phone}"}
if len(apv_dd_user_phone) == 1 and level_mode != "NONE":
return {"status": False, "msg": f"审核模式错误,一个审批人必须为'单人审批'"}
for _f, l in enumerate(level_data):
level_id = l["level_id"]
level_mode = l["level_mode"]
apv_dd_user_phone = l["apv_dd_user_phone"]
for i, p in enumerate(apv_dd_user_phone):
# 通过手机号获取发送人userid
resp = await get_id_by_phone(orgid=orgid, phone=p)
info(f"get_id_by_phone_resp:{resp}")
if not resp['status']:
return {"status": False, "msg": f"钉钉用户id获取失败,请检查手机号:{p}"}
dd_user_id = resp['user_id']
data = {
"user_id": user_id, # '用户id',
"orgid": orgid, # '机构id',
"business_id": business_id, # '业务id',
"level": level_id, # '节点级别',
"mode": level_mode, # 节点审核模式会签AND或签OR单人NONE',
"apv_dd_user_id_index": i, # '节点审核人员id index',
"apv_dd_user_id": dd_user_id, # '节点审核人员id',
"apv_dd_user_phone":p # '节点审核人员手机号',
}
if flag == "update":
if _f == 0 and i == 0:
sql = "update apv_flow set del_flg = 1 ,update_at=${update_at}$ where orgid=${orgid}$ and business_id=${business_id}$"
await sor.sqlExe(sql, {"orgid": orgid, "update_at": f"{datetime.datetime.now()}", "business_id": business_id})
else:
pass
else:
pass
data["id"] = uuid()
await sor.C("apv_flow", data)
return {"status": True, "msg": "保存成功"}
except Exception as e:
return {"status": False, "msg": f"数据库操作失败:{e}"}
return {"status": False, "msg": f"数据库操作失败"}
ret = await save_apv_flow(params_kw)
return ret

31
b/apv/save_apv_key.dspy Normal file
View File

@ -0,0 +1,31 @@
# 保存审批参数配置
async def save_apv_key(ns={}):
_f = ns.get("flag")
try:
data = {
"id": ns.get('id'),
"user_id": ns["user_id"], # 用户id
"orgid": ns["orgid"], # 机构id
"source": ns["source"], # key所属公司
"app_key": password_encode(ns["app_key"]), # 应用凭证key
"app_secret": password_encode(ns["app_secret"]), # 应用凭证secret
"http_aes_key": password_encode(ns["http_aes_key"]), # 加密aes_key
"http_token": password_encode(ns["http_token"]), # 签名token
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if _f == "add":
data["id"] = uuid()
await sor.C("apv_key", data)
elif _f == "update":
data["update_time"] = "{}".format(datetime.datetime.now())
await sor.U("apv_key", data)
else:
return {"status": False, "msg": "flag is error"}
return {"status": True, "msg": "success"}
ret = await save_apv_key(params_kw)
return ret

View File

@ -0,0 +1,46 @@
def get_process_code(url):
process_code = url.split('processCode')[-1].replace("%3D", "").replace("=", "")
# if len(process_code) != 41:
# return None
return process_code
# 保存审批参数配置
async def send_key_create(ns={}):
process_code = get_process_code(ns.get("process_code"))
if not process_code:
return {"status": False, "msg": "process_code,参数错误"}
try:
data = {
"id": ns["id"],
"user_id": ns["user_id"], # 用户id
"orgid": ns["orgid"], # 机构id
"send_dd_user_phone": ns["send_phone"], # 发送人的钉钉id
"source": ns["source"], # key所属公司
"business_id": ns["business_id"], # 业务id
"process_code": process_code, # 钉钉审批模板id
"process_name": ns["process_name"], # 钉钉表单名字
}
except Exception as e:
return {"status": False, "msg": f"参数解析错误,请检查参数:{e}"}
# 通过手机号获取发送人userid
resp = await get_id_by_phone(orgid=ns["orgid"], phone=ns["send_phone"])
if not resp['status']:
return {"status": False, "msg": f"{resp['msg']}\n钉钉用户id获取失败,请检查手机号:{ns['send_phone']}"}
data['send_dd_user_id'] = resp['user_id']
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if data['id']:
data["update_time"] = "{}".format(datetime.datetime.now())
await sor.U("apv_send_key", data)
else:
data["id"] = uuid()
await sor.C("apv_send_key", data)
return {"status": True, "msg": "success"}
ret = await send_key_create(params_kw)
return ret

View File

@ -0,0 +1,21 @@
# 获取审批流发送参数配置
async def send_key_select(ns={}):
try:
orgid = ns["orgid"]
business_id = ns["business_id"]
except Exception as e:
return {"status": False, "msg": f"servers get key error, please check key:{str(e)}"}
db = DBPools()
data = {}
async with db.sqlorContext('kboss') as sor:
data = await sor.R("apv_send_key", {"orgid": orgid, "del_flg": 0, "business_id": business_id})
if not data:
return {"status": False, "msg": "未找到发送参数配置"}
data = data[0]
return {"status": True, "data": data}
return {"status": False, "msg": "sql error"}
ret = await send_key_select(params_kw)
return ret

13
b/bill/addbill.dspy Normal file
View File

@ -0,0 +1,13 @@
async def addbill(ns):
"""添加账单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['id'] = uuid()
await sor.C('bill', ns)
return {'status': True, 'msg': '添加成功'}
return {'status': False, 'msg': '添加失败'}
ret = await addbill(params_kw)
return ret

12
b/bill/dlebill.dspy Normal file
View File

@ -0,0 +1,12 @@
async def dlebill(ns):
"""删除账单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['del_flg'] = '1'
await sor.U('bill', ns)
return {'status': True, 'msg': '删除成功'}
return {'status': False, 'msg': '删除失败'}
ret = await dlebill(params_kw)
return ret

17
b/bill/getbill.dspy Normal file
View File

@ -0,0 +1,17 @@
async def getbill(ns):
"""查询账单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['del_flg'] = '0'
ns['sort'] = 'create_at'
ns['order'] = 'desc'
reacs = await sor.R('bill',ns)
for i in reacs['rows']:
ns = {'id':i['customerid'],'del_fig':'0'}
customer = await sor.R('organization', ns)
i['orgname'] = customer[0]['orgname']
return {'status': True, 'data': reacs}
ret = await getbill(params_kw)
return ret

56
b/bill/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

11
b/bill/upbill.dspy Normal file
View File

@ -0,0 +1,11 @@
async def upbill(ns):
"""修改账单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
await sor.U('bill', ns)
return {'status': True, 'msg': '修改成功'}
return {'status': False, 'msg': '修改失败'}
ret = await upbill(params_kw)
return ret

4536
b/bricks/bricks.js Normal file

File diff suppressed because it is too large Load Diff

1
b/bricks/bricks.min.js vendored Normal file

File diff suppressed because one or more lines are too long

237
b/bricks/css/bricks.css Normal file
View File

@ -0,0 +1,237 @@
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
}
.griddata {
display: grid;
grid-gap: 1px;
}
.modal {
padding: 10px;
color: #e8e8e8;
background-color: rgba(0.3, 0.3, 0.3, 1);
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.modal>.title {
background-color: #a0a0a0;
}
.message {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.error {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.message>.title {
background-color: #3030f0;
}
.vscroll {
overflow-x: scroll;
}
.hscroll {
overflow-y: scroll;
}
.scroll {
overflow:scroll;
}
.error>.title {
background-color: #f03030;
}
.vbox {
display: flex;
flex-direction: column;
}
.hbox {
display: flex;
flex-direction: row;
}
.fixitem {
flex:none;
}
.filler, .hfiller, .vfiller {
flex: auto;
scroll:auto;
}
.vfiller .vbox:last-child {
overflow-x: overlay;
}
.vline {
height:1px;
background-colir:#999;
}
.hline {
height:1px;
background-colir:#999;
}
.hfiller::-webkit-scrollbar {
display: none;
}
.flc {
width: 203px;
overflow-y: scroll;
overflow-x: visible;
}
.vtoolbar {
heigth: 100%;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.selected {
background-color: #d4d4d4;
}
.htoolbar {
width: 100%;
height: 40px;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.toolbar-button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
border: 1px solid #888;
}
.toolbar-button-active {
background-color: #ddd;
}
.tabpanel {
background-color: #ededed;
border: 3px solid #888;
}
.tabpanel-content {
background-color: #f8f8f8;
border: 2px solid #888;
}
.multicolumns {
column-width: 340px;
colomn-gap: 10px'
}
.popup {
z-index: 1000;
position: absolution;
background-color: #f1f1f1;
border: 1px solid #c1c1c1;
}
.inputbox {
background-color: #cccccc;
border: 1px solid #bbbbbb;
padding: 10px;
margin: 0 0 1em 0;
}
button[tooltip]:hover::after {
content: attr(tooltip);
display: block;
position: absolute;
transform: translateX(-50%);
}
div[tooltip]:hover::after {
content: attr(tooltip);
display: block;
position: absolute;
transform: translateX(-50%);
}
input[tooltip],
div[tooltip] {
width: max-content;
margin: auto;
}
.datagrid {
display:flex;
flex-direction:column;
width:100%;
height:100%;
}
.datagrid-grid {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: row;
}
.datagrid-left {
height:100%;
display: flex;
flex-direction: column;
overflow: auto;
}
.datagrid-left>.scrollbar {
width:0px;
opacity:0;
}
.datagrid-right {
flex:1 0 ;
height:100%;
overflow: auto;
display: flex;
flex-direction: column;
}
.grid_header, .grid_footer {
height: 50px;
background-color: blue;
}
.datagrid-row {
flex:0 0 150px;
display: flex;
flex-direction: row;
}
.datagrid-body {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}

BIN
b/bricks/imgs/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
b/bricks/imgs/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
b/bricks/imgs/down_dir.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
b/bricks/imgs/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
b/bricks/imgs/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
b/bricks/imgs/move_down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
b/bricks/imgs/move_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
b/bricks/imgs/move_up.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
b/bricks/imgs/reset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
b/bricks/imgs/submit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

56
b/bricks/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,58 @@
async def addbz_order(ns):
"""
生成订单 立即下单
userid : 用户id
`goods`'产品id',
`spec_id` '规格id',
`quantity`'数量',
`transname` '交易名称',
`providerid` '供应商id',
`order_status` '0:未支付1:已交付2:已关闭3:已取消4:后付费',
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
try:
users_id = await get_user()
user = await sor.R('users', {'id': users_id})
orgid = await sor.R('organization', {'id': user[0]['orgid']})
amountadll = 0
bz_ns = {}
# bz_ns['id'] = UUID()
bz_ns['id'] = uuid()
bz_ns['order_status'] = '0'
bz_ns['business_op'] = 'BUY'
bz_ns['userid'] = ns.get('userid')
bz_ns['customerid'] = orgid[0]['id']
bz_ns['order_date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if ns.get('specdataid'):
bz_ns['specdataid'] = ns['specdataid']
await sor.C('bz_order', bz_ns)
# 添加账户表
nss = {}
nss['id'] = uuid()
# nss['id'] = UUID()
nss['orderid'] = bz_ns['id']
nss['productid'] = ns.get('productid')
nss['providerid'] = ns.get('providerid')
nss['list_price'] = ns.get('list_price')
nss['discount'] = ns.get('discount')
nss['quantity'] = ns.get('quantity')
nss['price'] = ns.get('amount')
nss['spec_id'] = ns.get('spec_id')
nss['unit'] = ns.get('unit')
nss['chargeduration'] = ns.get('chargeduration')
if int(ns.get('quantity')) > 1:
nss['amount'] = float(ns['amount']) * int(ns['quantity'])
amountadll += nss['amount']
else:
nss['amount'] = ns['amount']
amountadll += float(nss['amount'])
await sor.C('order_goods', nss)
await sor.U('bz_order', {'id': bz_ns['id'], 'amount': amountadll})
return {'status': True, 'msg': '添加成功', 'bz_id': bz_ns['id']}
except Exception as error:
raise error
return {'status': False, 'msg': '添加失败'}
ret = await addbz_order(params_kw)
return ret

View File

@ -0,0 +1,158 @@
async def cal_expire_time(chargeduration=None, unit=None):
chargeduration = int(chargeduration)
# 当前时间
now = datetime.datetime.now()
if unit == 'day':
expire_time = now + dateutil.relativedelta.relativedelta(days=chargeduration)
elif unit == 'week':
expire_time = now + dateutil.relativedelta.relativedelta(weeks=chargeduration)
elif unit == 'month':
expire_time = now + dateutil.relativedelta.relativedelta(months=chargeduration)
elif unit == 'quarter':
expire_time = now + dateutil.relativedelta.relativedelta(months=chargeduration * 3)
elif unit == 'year':
expire_time = now + dateutil.relativedelta.relativedelta(years=chargeduration)
else:
expire_time = None
if expire_time:
return str(expire_time)
else:
return None
async def affirmbz_order(ns):
"""确认支付"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
# 查询产品说明是否是product_sync
order_productid_providerid = await sor.R('order_goods', {'orderid': ns['orderid']})
order_productid = order_productid_providerid[0]['productid']
order_providerid = order_productid_providerid[0]['providerid']
description_li = await sor.R('product', {'id': order_productid})
description = description_li[0]['description'] if description_li else []
classify_li = await sor.R('product', {'id': order_productid})
classify = classify_li[0]['classify'] if classify_li else ''
if description == 'product_sync':
# 根据orderid在order_goods里查询对应账单
# order_providerid = (await sor.R('order_goods', {'orderid': ns['orderid']}))[0]['providerid']
# 查询阿里云得id
ali_id_li = await sor.R('organization', {'orgname': '阿里云'})
if ali_id_li:
ali_id = ali_id_li[0]['id']
else:
ali_id = None
# 查询开元云得id
k_id_li = await sor.R('organization', {'orgname': '开元云'})
if k_id_li:
k_id = k_id_li[0]['id']
else:
k_id = None
target_id = None
if order_providerid == ali_id:
target_id = ali_id
if order_providerid == k_id:
target_id = k_id
if target_id:
# 查询业主机构针对所有客户的普通协议
protocolid = (await sor.R('saleprotocol', {'offer_orgid': 'mIWUHBeeDM8mwAFPIQ8pS', 'bid_orgid': '*', 'salemode': '2', 'del_flg': '0'}))[0]['id']
# 在产品价格表中更新价格配置
id_sql = """select id, price from product_salemode where protocolid = '%s' and providerid = '%s' and productid = '%s' order by price desc limit 1;""" % (protocolid, target_id, order_productid)
id_find = (await sor.sqlExe(id_sql, {}))[0]['id']
# 查询价格
current_price = (await sor.R('bz_order', {'id': ns['orderid']}))[0]['amount']
await sor.U('product_salemode', {'id': id_find, 'price': current_price})
orgid = await sor.R('bz_order', {'id': ns['orderid']})
date = await get_business_date(sor=None)
await sor.U('bz_order',{'id':ns['orderid'],'order_date': date})
count = await getCustomerBalance(sor, orgid[0]['customerid'])
if count == None:
count = 0
if count - float(orgid[0]['amount']) < 0:
pricedifference = count - round(orgid[0]['amount'],2)
return {'status': False, 'msg': '账户余额不足','pricedifference': round(pricedifference,2)}
await order2bill(ns['orderid'], sor)
bills = await sor.R('bill', {'orderid': ns['orderid'], 'del_flg': '0'})
try:
# 需要加事务
for i in bills:
ba = BillAccounting(i)
r = await ba.accounting(sor)
dates = datetime.datetime.now()
await sor.U('bz_order', {'id': ns['orderid'], 'order_status': '1','create_at':dates})
await sor.U('bill', {'id': ns['orderid'], 'bill_state': '1'})
order_goods = await sor.R('order_goods', {'orderid': ns['orderid']})
for j in order_goods:
# 计算过期时间
chargeduration = j.get('chargeduration')
unit = j.get('unit')
if chargeduration and unit:
expire_time = await cal_expire_time(chargeduration=chargeduration, unit=unit)
else:
expire_time = None
product = await sor.R('product', {'id': j['productid']})
nss = {}
nss['id'] = uuid()
# nss['id'] = UUID()
nss['providerrid'] = product[0]['providerid']
nss['productname'] = product[0]['name']
nss['productdesc'] = product[0]['description']
nss['customerid'] = orgid[0]['customerid']
nss['productid'] = product[0]['id']
nss['specdataid'] = j['spec_id']
nss['orderid'] = orgid[0]['id']
nss['start_date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if expire_time:
nss['expire_date'] = expire_time
if ns.get('product_url'):
nss['product_url'] = ns.get('product_url')
await sor.C('customer_goods', nss)
v = {}
v['orderid'] = orgid[0]['id']
v['customerid'] = orgid[0]['customerid']
v['providerid'] = product[0]['providerid']
v['productid'] = product[0]['id']
v['quantity'] = j['quantity']
await path_call('../pub/hpc_save_bill.dspy', v)
# k8s产品 支付成功后创建实例
if classify == 'E':
spec = json.loads(product[0]['spec_note']) if isinstance(product[0]['spec_note'], str) else product[0]['spec_note']
# 映射表,用于将中文字段名转换为英文
mapping = {
"CPU": "cpu",
"内存": "memory",
"存储": "storage",
"GPU": "gpu",
"显存": "gpumem"
}
# 转换成目标格式的字典
k8s_pod_create_ns = {mapping[item["configName"]]: int(item["value"]) for item in spec}
k8s_pod_create_ns['orgid'] = orgid[0]['customerid']
k8s_pod_create_ns['image'] = 'ubuntu'
k8s_pod_create_ns['productid'] = product[0]['id']
k8s_pod_create_ns['productname'] = product[0]['name']
k8s_pod_create_ns['customer_goods_id'] = nss['id']
ks8_result = await path_call('../pub/user_create_pod.dspy', k8s_pod_create_ns)
if ks8_result['status']:
return {
'status': True,
'msg': '实例创建成功, 支付操作成功'
}
else:
await sor.rollback()
print('k8s实例创建失败, 回滚支付操作')
return {
'status': False,
'msg': '实例创建失败, 回滚支付操作'
}
return {'status': True, 'msg': '支付成功'}
except Exception as error:
raise error
else:
return {'status': False, 'msg': '参数错误'}
return {'status': False, 'msg': '支付失败'}
ret = await affirmbz_order(params_kw)
return ret

View File

@ -0,0 +1,18 @@
async def cancelbz_order(ns):
"""取消订单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
try:
# 需要加事务
await sor.U('bz_order', {'id': ns['orderid'], 'order_status': '3'})
return {'status': True, 'msg': '取消订单'}
except Exception as error:
raise error
else:
return {'status': False, 'msg': '参数错误'}
return {'status': False, 'msg': '取消失败'}
ret = await cancelbz_order(params_kw)
return ret

View File

@ -0,0 +1,24 @@
async def chargeback(ns):
""" 退单
id:订单的id
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
await sor.U('bz_order', {'id':ns['id'],'business_op':'BUY_REVERSE','order_status':'0'})
await order2bill(ns['id'], sor)
bills = await sor.R('bill', {'orderid':ns.get('id')})
for i in bills:
ba = BillAccounting(i)
r = await ba.accounting(sor)
order_goods = await sor.R('customer_goods', {'orderid': ns['id']})
for j in order_goods:
await sor.U('customer_goods', {'id': j['id'],'del_flg':'1'})
await sor.U('bz_order', {'id': ns['id'], 'order_status':'3'})
return {'status': True, 'msg': '成功'}
except Exception as e:
raise e
return {'status': False, 'msg': '失败'}
ret = await chargeback(params_kw)
return ret

View File

@ -0,0 +1,51 @@
async def customer_cartpay(ns):
""" 购物车下单
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
customer_cart = await sor.R('customer_cart', {'id':ns['id']})
amount = 0
count = 0
bz_ns = {}
# bz_ns['id'] = UUID()
bz_ns['id'] = uuid()
for i in eval(ns.get('goods')):
count += 1
cart_goods = await sor.R('cart_goods', {'id': i})
amount += float(cart_goods[0]['amount'])
nss = {}
nss['id'] = uuid()
# nss['id'] = UUID()
nss['orderid'] = bz_ns['id']
nss['productid'] = cart_goods[0]['productid']
product = await sor.R('product', {'id': cart_goods[0]['productid']})
nss['providerid'] = product[0]['providerid']
nss['list_price'] = cart_goods[0].get('list_price')
nss['discount'] = cart_goods[0].get('discount')
nss['quantity'] = cart_goods[0].get('quantity')
nss['price'] = cart_goods[0].get('price')
nss['spec_id'] = cart_goods[0].get('spec_id')
nss['amount'] = cart_goods[0].get('amount')
await sor.C('order_goods', nss)
sql = """ UPDATE cart_goods SET del_flg = '1' WHERE id = ${id}$ """
nsa = {'id': i}
await sor.sqlExe(sql, nsa)
bz_ns['order_status'] = '0'
bz_ns['business_op'] = 'BUY'
bz_ns['amount'] = amount
bz_ns['customerid'] = customer_cart[0]['customerid']
bz_ns['order_date'] = datetime.datetime.now().strftime("%Y-%m-%d")
await sor.C('bz_order', bz_ns)
olen = customer_cart[0]['goods_cnt']
ncount = olen - count
oamount = float(customer_cart[0]['amount'])
namount = oamount - amount
await sor.U('customer_cart', {'id': ns['id'],'amount':namount,'goods_cnt':ncount})
return {'status': True, 'msg': '添加成功', 'bz_id': bz_ns['id']}
except Exception as e:
raise e
return {'status': False, 'msg': '添加失败'}
ret = await customer_cartpay(params_kw)
return ret

View File

@ -0,0 +1,12 @@
async def dlebz_order(ns):
"""删除订单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
ns['del_flg'] = '1'
await sor.U('bz_order', ns)
return {'status': True, 'msg': '删除成功'}
return {'status': False, 'msg': '删除失败'}
ret = await dlebz_order(params_kw)
return ret

198
b/bz_order/getbz_order.dspy Normal file
View File

@ -0,0 +1,198 @@
async def getbz_order(ns={}):
"""查询订单商品详情,带分页功能"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
users_id = await get_user()
if not users_id:
server_error(401)
# 分页参数
page = int(ns.get('page', 1))
size = int(ns.get('size', 10))
offset = (page - 1) * size
user = await sor.R('users', {'id': users_id, 'del_flg': '0'})
orgid = await sor.R('organization', {'id': user[0]['orgid'], 'del_flg': '0'})
customerid = orgid[0]['id']
params = {'customerid': customerid, 'del_flg': '0'}
# 构建查询SQL主要查询order_goods并关联bz_order的部分字段
sql = """
SELECT
og.*,
bo.customerid,
bo.order_date,
bo.source,
bo.order_status,
bo.business_op,
bo.ordertype,
bo.originalprice,
bo.autoreneworder
FROM order_goods og
JOIN bz_order bo ON og.orderid = bo.id
WHERE og.del_flg = '0'
AND bo.del_flg = '0'
AND bo.customerid = ${customerid}$
"""
# 构建计数SQL
count_sql = """
SELECT COUNT(*) as total_count
FROM order_goods og
JOIN bz_order bo ON og.orderid = bo.id
WHERE og.del_flg = '0'
AND bo.del_flg = '0'
AND bo.customerid = ${customerid}$
"""
# # 统计全部 累计支付金额和累计优惠金额 不包含各种筛选条件
# # 累计支付金额=BUY+RENEW-BUY_REVERSE实际支付金额 累计优惠金额=BUY+RENEW-BUY_REVERSE优惠金额
# total_amount_sql = """
# SELECT
# COALESCE(SUM(
# CASE
# WHEN bo.business_op IN ('BUY', 'RENEW') THEN og.amount
# WHEN bo.business_op = 'BUY_REVERSE' THEN -og.amount
# ELSE 0
# END
# ), 0) AS total_paid_amount,
# COALESCE(SUM(
# CASE
# WHEN bo.business_op IN ('BUY', 'RENEW') THEN (og.list_price * og.quantity - og.amount)
# WHEN bo.business_op = 'BUY_REVERSE' THEN -(og.list_price * og.quantity - og.amount)
# ELSE 0
# END
# ), 0) AS total_discount_amount
# FROM order_goods og
# JOIN bz_order bo ON og.orderid = bo.id
# WHERE og.del_flg = '0'
# AND bo.del_flg = '0'
# AND bo.customerid = ${customerid}$
# """
# total_amount_result = await sor.sqlExe(total_amount_sql, {'customerid': customerid})
# total_paid_amount = float(total_amount_result[0]['total_paid_amount']) if total_amount_result else 0.0
# total_discount_amount = float(total_amount_result[0]['total_discount_amount']) if total_amount_result else 0.0
# # 将累计支付金额和累计优惠金额添加到返回结果中
# ns['total_paid_amount'] = total_paid_amount
# ns['total_discount_amount'] = total_discount_amount
# ns['total_count'] = total_count[0]['total_count'] if total_count else 0
# 根据订单号搜索
if ns.get('id'):
sql += " AND bo.id LIKE ${order_id}$"
count_sql += " AND bo.id LIKE ${order_id}$"
params['order_id'] = f"%{ns.get('id')}%"
# 时间范围查询
if ns.get('start_time'):
sql += " AND bo.order_date >= ${start_time}$"
count_sql += " AND bo.order_date >= ${start_time}$"
params['start_time'] = ns.get('start_time') + ' 00:00:00'
if ns.get('end_time'):
sql += " AND bo.order_date <= ${end_time}$"
count_sql += " AND bo.order_date <= ${end_time}$"
params['end_time'] = ns.get('end_time') + ' 23:59:59'
# 订单状态查询
if ns.get('order_status'):
sql += " AND bo.order_status = ${order_status}$"
count_sql += " AND bo.order_status = ${order_status}$"
params['order_status'] = ns.get('order_status')
# 业务操作筛选business_op BUY, RENEW, BUY_REVERSE
if ns.get('business_op'):
sql += " AND bo.business_op = ${business_op}$"
count_sql += " AND bo.business_op = ${business_op}$"
params['business_op'] = ns.get('business_op')
# 添加排序
sql += " ORDER BY bo.order_date DESC"
# 获取总记录数
total_result = await sor.sqlExe(count_sql, params)
total_count = total_result[0]['total_count'] if total_result else 0
# 添加分页
sql += " LIMIT ${size}$ OFFSET ${offset}$"
params['size'] = size
params['offset'] = offset
# 执行查询
order_goods_list = await sor.sqlExe(sql, params)
if not order_goods_list:
return {
'status': True,
'data': [],
'pagination': {'page': page, 'size': size, 'total': 0}
}
# 处理数据
for item in order_goods_list:
# 清理折扣数据
if item['discount'] is None or item['discount'] == 1.0:
del item['discount']
else:
item['discount'] = item['discount'] * 10 # 转换为百分比显示
# 处理结果中原价list_price和数量quantity 计算总价
if item['list_price'] is None:
item['list_price'] = 0
if item['quantity'] is None:
item['quantity'] = 0
item['origin_amout'] = float(item['list_price']) * float(item['quantity'])
# 获取产品名称
goods = await sor.R('product', {'id': item['productid'], 'del_flg': '0'})
if len(goods) >= 1:
item['product_name'] = goods[0]['name']
item['product_type'] = goods[0]['ptype']
# 获取产品配置详情
item['spec_data'] = None
if item.get('spec_id'):
specdata_list = await sor.R('specificdata', {'id': item['spec_id'], 'del_flg': '0'})
if specdata_list and specdata_list[0]['spec_data']:
item['spec_data'] = json.loads(specdata_list[0]['spec_data'])
# 格式化订单状态
status_mapping = {
'0': '未支付',
'1': '已支付',
'2': '已关闭',
'3': '已取消',
'4': '后付费'
}
item['order_status_text'] = status_mapping.get(item['order_status'], '未知状态')
# 格式化业务操作
business_op_mapping = {
'BUY': '购买',
'RENEW': '续费',
'BUY_REVERSE': '退款',
# 'UPGRADE': '升级'
}
item['business_op_text'] = business_op_mapping.get(item['business_op'], item['business_op'])
return {
'status': True,
'data': order_goods_list,
'pagination': {
'page': page,
'size': size,
'total': total_count
}
}
except Exception as e:
import traceback
traceback.print_exc()
return {'status': False, 'msg': '信息错误: %s' % str(e) + traceback.format_exc()}
ret = await getbz_order(params_kw)
return ret

View File

@ -0,0 +1,110 @@
async def getbz_order(ns={}):
"""查询订单详情/我的所有订单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
users_id = await get_user()
ns['del_flg'] = '0'
ns['sort'] = 'create_at desc'
if ns.get('id') and not ns.get('type'):
# 查询订单详情
reacs = await sor.R('bz_order', ns)
# 兼容第三范式产品展示 这段代码可注释
if reacs and reacs[0].get('specdataid'):
specdata = await sor.R('specificdata', {'id': reacs[0].get('specdataid'), 'del_flg': '0'})
if specdata:
specdata_dumps = json.dumps(specdata[0]) if isinstance(specdata[0], dict) else specdata[0]
if 'internalip' in specdata_dumps and 'externalip' in specdata_dumps:
reacs[0]['spec_data'] = json.loads(json.loads(specdata_dumps)['spec_data'])
reacsgoods = await sor.R('order_goods', {'orderid': ns['id'], 'del_flg': '0'})
for i in reacsgoods:
if i['discount'] == None or i['discount'] == 1.0:
del i['discount']
i['discount'] = i['discount'] * 10 if i.get('discount') else None
goods = await sor.R('product', {'id': i['productid'], 'del_flg': '0'})
if len(goods) >= 1:
i['productid'] = goods[0]['name']
i['ptype'] = goods[0]['ptype']
reacs[0]['order_goods'] = reacsgoods
return {'status': True, 'data': reacs}
elif ns.get('type') == '200':
#筛选支付类型
user = await sor.R('users', {'id': users_id, 'del_flg': '0'})
orgid = await sor.R('organization', {'id': user[0]['orgid'], 'del_flg': '0'})
ns['customerid'] = orgid[0]['id']
if ns.get('id'):
#根据订单号搜索
reacs = await sor.R('bz_order', {'id': ns['id']})
elif ns.get('start_time'):
sql = """select * from bz_order where del_flg = 0 AND customerid = ${customerid}$ AND order_date >= ${start_time}$ AND order_date <= ${end_time}$ ORDER BY order_date DESC """
start_time = ns.get('start_time') + ' 00:00:00'
end_time = ns.get('end_time') + ' 23:59:59'
reacs = await sor.sqlExe(sql, {'start_time': start_time,'end_time': end_time,'customerid' :ns['customerid']})
else:
reacs = await sor.R('bz_order', {'customerid':ns['customerid'],'sort':'order_date desc','order_status':ns.get('order_status'),'del_flg':'0'})
all_price = 0
if len(reacs) >= 1:
for j in reacs:
reacsgoods = await sor.R('order_goods', {'orderid': j['id'], 'del_flg': '0'})
countlist_price = 0
for i in reacsgoods:
if i['discount'] == None or i['discount'] == 1.0:
del i['discount']
goods = await sor.R('product', {'id': i['productid'], 'del_flg': '0'})
if len(goods) >= 1:
i['productid'] = goods[0]['name']
j['order_goods'] = reacsgoods
countlist_price += i['list_price']
countlist_price *= i['quantity']
if j['order_status'] == '1':
all_price += countlist_price
j['countprice'] = round(countlist_price,2)
if j['countprice'] == 0:
j['countprice'] = j['originalprice']
return {'status': True, 'data': reacs, 'all_price': round(all_price, 2)}
else:
# 我的所有订单
ns['del_flg'] = '0'
ns['sort'] = 'create_at desc'
user = await sor.R('users', {'id': users_id, 'del_flg': '0'})
orgid = await sor.R('organization', {'id': user[0]['orgid'], 'del_flg': '0'})
ns['customerid'] = orgid[0]['id']
reacs = await sor.R('bz_order', {'customerid':ns['customerid'],'sort':'create_at desc','del_flg':'0'})
actual_all_price = 0
for j in reacs:
reacsgoods = await sor.R('order_goods', {'orderid': j['id'], 'del_flg': '0'})
all_price = 0
countlist_price = 0
business_op = j['business_op']
for i in reacsgoods:
if i['discount'] == None or i['discount'] == 1.0:
del i['discount']
i['discount'] = i['discount'] * 10 if i.get('discount') else None
goods = await sor.R('product', {'id': i['productid'], 'del_flg': '0'})
if len(goods) < 1:
continue
i['ptype'] = goods[0]['ptype']
if len(goods) >= 1:
i['productid'] = goods[0]['name']
j['order_goods'] = reacsgoods
countlist_price = i['list_price'] * i['quantity']
actual_price = i['price'] * i['quantity']
# countlist_price += i['list_price']
# countlist_price *= i['quantity']
if j['order_status'] == '1':
all_price += countlist_price
if business_op != 'BUY_REVERSE':
actual_all_price += actual_price
j['countprice'] = round(all_price,2)
if j['countprice'] == 0:
j['countprice'] = j['originalprice']
return {'status': True, 'data': reacs,'all_price': round(actual_all_price,2)}
except Exception as e:
raise e
return {'status': False, 'msg': '信息错误'}
ret = await getbz_order(params_kw)
return ret

56
b/bz_order/index.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE HTML>
<style type="text/css">
body{
font-family: 'Courgette', cursive;
}
body{
background:#f3f3e1;
}
.wrap{
margin:0 auto;
width:1000px;
}
.logo{
margin-top:50px;
}
.logo h1{
font-size:200px;
color:#8F8E8C;
text-align:center;
margin-bottom:1px;
text-shadow:1px 1px 6px #fff;
}
.logo p{
color:rgb(228, 146, 162);
font-size:20px;
margin-top:1px;
text-align:center;
}
.logo p span{
color:lightgreen;
}
.sub a{
color:white;
background:#8F8E8C;
text-decoration:none;
padding:7px 120px;
font-size:13px;
font-family: arial, serif;
font-weight:bold;
-webkit-border-radius:3em;
-moz-border-radius:.1em;
-border-radius:.1em;
}
</style>
<body>
<div class="wrap">
<div class="logo">
<h1>404</h1>
<p>The Page not Found</p>
<div class="sub">
<p><a href="/">Back</a></p>
</div>
</div>
</div>
</body>

View File

@ -0,0 +1,11 @@
async def upbz_order(ns):
"""修改订单"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
if ns:
await sor.U('bz_order', ns)
return {'status': True, 'msg': '修改成功'}
return {'status': False, 'msg': '修改失败'}
ret = await upbz_order(params_kw)
return ret

0
b/cpcc/__init__.py Normal file
View File

Some files were not shown because too many files have changed in this diff Show More